use crate::{
ParsePhdrError, Result,
arch::NativeArch,
elf::{ElfDyn, ElfHeader, ElfLayout, ElfPhdr, ElfPhdrs, ElfProgramType, NativeElfLayout},
input::{ElfReader, PathBuf},
memory::{ImageMemory, MappedView, RegionAccess, VmAddr},
os::ProtFlags,
relocation::RelocationArch,
runtime::CodeExecutor,
segment::{ElfSegments, MemoryProtection},
sync::Arc,
tls::{TlsInfo, TlsResolver},
};
use alloc::{boxed::Box, vec::Vec};
use core::{marker::PhantomData, ptr::NonNull};
pub(crate) struct ImageBuilder<
Tls,
D: 'static = (),
Arch: RelocationArch = NativeArch,
R: RegionAccess = crate::memory::HostRegion,
> where
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
{
pub(crate) path: PathBuf,
ehdr: Option<ElfHeader<Arch::Layout>>,
pub(crate) entry: VmAddr,
pub(crate) relro: Option<MemoryProtection>,
pub(crate) dynamic: Option<MappedView<ElfDyn<Arch::Layout>>>,
pub(crate) dynamic_addr: Option<VmAddr>,
pub(crate) tls_info: Option<TlsInfo>,
pub(crate) static_tls: bool,
page_size: usize,
pub(crate) user_data: D,
pub(crate) executor: Arc<dyn CodeExecutor<Arch>>,
pub(crate) segments: ElfSegments<R>,
pub(crate) interp: Option<&'static str>,
pub(crate) eh_frame_hdr: Option<NonNull<u8>>,
_marker: PhantomData<fn() -> Tls>,
}
pub(crate) struct ScanBuilder<L: ElfLayout = NativeElfLayout> {
pub(crate) path: PathBuf,
pub(crate) ehdr: ElfHeader<L>,
pub(crate) phdrs: Box<[ElfPhdr<L>]>,
pub(crate) reader: Box<dyn ElfReader + 'static>,
}
impl<L: ElfLayout> ScanBuilder<L> {
#[inline]
pub(crate) fn new(
path: PathBuf,
ehdr: ElfHeader<L>,
phdrs: Box<[ElfPhdr<L>]>,
reader: Box<dyn ElfReader + 'static>,
) -> Self {
Self {
path,
ehdr,
phdrs,
reader,
}
}
}
impl<Tls, D: 'static, Arch, R> ImageBuilder<Tls, D, Arch, R>
where
Tls: TlsResolver<Arch>,
Arch: RelocationArch,
R: RegionAccess,
{
pub(crate) fn new(
segments: ElfSegments<R>,
path: PathBuf,
ehdr: Option<ElfHeader<Arch::Layout>>,
entry: VmAddr,
static_tls: bool,
page_size: usize,
user_data: D,
executor: Arc<dyn CodeExecutor<Arch>>,
) -> Self {
Self {
path,
ehdr,
entry,
relro: None,
dynamic: None,
dynamic_addr: None,
tls_info: None,
static_tls,
page_size,
segments,
user_data,
executor,
interp: None,
eh_frame_hdr: None,
_marker: PhantomData,
}
}
pub(crate) fn parse_phdr(&mut self, phdr: &ElfPhdr<Arch::Layout>) -> Result<()> {
match phdr.program_type() {
ElfProgramType::DYNAMIC => {
self.dynamic_addr = Some(self.segments.base() + phdr.p_vaddr());
self.dynamic = Some(
self.segments
.read_view::<ElfDyn<Arch::Layout>>(phdr.p_vaddr(), phdr.p_filesz())
.ok_or_else(|| {
ParsePhdrError::malformed(
"PT_DYNAMIC is not directly readable from mapped segments",
)
})?,
)
}
ElfProgramType::GNU_RELRO => {
self.relro = Some(MemoryProtection::new(
self.segments.base() + phdr.p_vaddr(),
phdr.p_memsz(),
self.page_size,
ProtFlags::PROT_READ,
))
}
ElfProgramType::PHDR => {}
ElfProgramType::INTERP => {
self.interp = Some(self.read_interp(phdr)?);
}
ElfProgramType::GNU_EH_FRAME => {
self.eh_frame_hdr = Some(
self.segments
.host_ptr_range(self.segments.base() + phdr.p_vaddr(), phdr.p_filesz())
.ok_or_else(|| {
ParsePhdrError::malformed(
"PT_GNU_EH_FRAME is not directly readable from mapped segments",
)
})?,
);
}
ElfProgramType::TLS => {
self.segments
.read_view::<u8>(phdr.p_vaddr(), phdr.p_filesz())
.ok_or_else(|| ParsePhdrError::malformed("PT_TLS image is malformed"))?;
self.tls_info = Some(TlsInfo::new(phdr));
}
_ => {}
};
Ok(())
}
fn read_interp(&self, phdr: &ElfPhdr<Arch::Layout>) -> Result<&'static str> {
let view = self
.segments
.read_view::<u8>(phdr.p_vaddr(), phdr.p_filesz())
.ok_or_else(|| {
ParsePhdrError::malformed("PT_INTERP is not directly readable from mapped segments")
})?;
let bytes = view.as_slice();
let Some(nul) = bytes.iter().position(|byte| *byte == 0) else {
return Err(ParsePhdrError::malformed("PT_INTERP is missing a NUL terminator").into());
};
if nul == 0 {
return Err(ParsePhdrError::malformed("PT_INTERP is empty").into());
}
core::str::from_utf8(&bytes[..nul])
.map_err(|_| ParsePhdrError::InvalidUtf8 { field: "PT_INTERP" }.into())
}
pub(crate) fn parse_phdrs(&mut self, phdrs: &[ElfPhdr<Arch::Layout>]) -> Result<()> {
for phdr in phdrs {
self.parse_phdr(phdr)?;
}
Ok(())
}
pub(crate) fn create_phdrs(
&self,
phdrs: &[ElfPhdr<Arch::Layout>],
) -> Result<ElfPhdrs<Arch::Layout>> {
for phdr in phdrs {
if phdr.program_type() != ElfProgramType::PHDR {
continue;
}
if let Some(mapped) = self
.segments
.read_view::<ElfPhdr<Arch::Layout>>(phdr.p_vaddr(), phdr.p_memsz())
{
return Ok(ElfPhdrs::Mapped(mapped));
}
}
let Some(ehdr) = &self.ehdr else {
return Ok(ElfPhdrs::Vec(Vec::from(phdrs)));
};
let Some((phdr_start, phdr_end)) = ehdr.phdr_range()? else {
return Ok(ElfPhdrs::Vec(Vec::from(phdrs)));
};
let phdr_size = phdr_end - phdr_start;
for phdr in phdrs {
if phdr.program_type() != ElfProgramType::LOAD {
continue;
}
let seg_start = phdr.p_offset();
let Some(seg_end) = seg_start.checked_add(phdr.p_filesz()) else {
continue;
};
if seg_start <= phdr_start && phdr_end <= seg_end {
let Some(phdr_vaddr) = phdr.p_vaddr().checked_add(phdr_start - seg_start) else {
continue;
};
if let Some(mapped) = self
.segments
.read_view::<ElfPhdr<Arch::Layout>>(phdr_vaddr, phdr_size)
{
return Ok(ElfPhdrs::Mapped(mapped));
}
}
}
Ok(ElfPhdrs::Vec(Vec::from(phdrs)))
}
}