use crate::{
AlignedBytes, ParseDynamicError, ParsePhdrError, Result,
arch::NativeArch,
elf::{
ElfDyn, ElfHeader, ElfLayout, ElfPhdr, ElfProgramType, ElfSectionFlags, ElfSectionIndex,
ElfSectionType, ElfShdr, ElfStringTable, NativeElfLayout, parse_dynamic_entries,
},
entity::entity_ref,
input::{ElfReader, ElfReaderExt, Path, PathBuf},
loader::ScanBuilder,
relocation::RelocationArch,
};
use alloc::{boxed::Box, vec::Vec};
use core::{fmt, mem::size_of, num::NonZeroUsize, ptr};
use elf::abi::{DF_1_NOW, DF_BIND_NOW, DF_STATIC_TLS};
struct DynamicScanParts {
dynamic: ScannedDynamicInfo,
strtab: Box<[u8]>,
needed_libs: Box<[usize]>,
soname: Option<usize>,
rpath: Option<usize>,
runpath: Option<usize>,
}
impl DynamicScanParts {
fn new<L: ElfLayout>(object: &mut dyn ElfReader, phdrs: &[ElfPhdr<L>]) -> Result<Self> {
let dynamic_phdr = phdrs
.iter()
.find(|phdr| phdr.program_type() == ElfProgramType::DYNAMIC)
.ok_or(ParsePhdrError::MissingDynamicSection)?;
if dynamic_phdr.p_filesz() % size_of::<ElfDyn<L>>() != 0 {
return Err(
ParsePhdrError::malformed("PT_DYNAMIC size is not a multiple of ElfDyn").into(),
);
}
let dyns = object.read_to_vec::<ElfDyn<L>>(
dynamic_phdr.p_offset(),
dynamic_phdr.p_filesz() / core::mem::size_of::<ElfDyn<L>>(),
)?;
let parsed = parse_dynamic_entries(
dyns.into_iter()
.map(|dynamic| (dynamic.tag(), dynamic.value())),
);
let strtab_vaddr =
NonZeroUsize::new(parsed.strtab_off).ok_or(ParseDynamicError::AddressOverflow)?;
let strtab_file_off = vaddr_to_file_offset(strtab_vaddr.get(), phdrs)?;
let strtab_size = parsed
.strtab_size
.ok_or(ParseDynamicError::MissingRequiredTag { tag: "DT_STRSZ" })?
.get();
let strtab = object.read_to_vec(strtab_file_off, strtab_size)?;
let needed_libs = parsed
.needed_libs
.into_iter()
.map(|offset| offset.get())
.collect::<Vec<_>>()
.into_boxed_slice();
let soname = parsed.soname_off.map(|offset| offset.get());
let rpath = parsed.rpath_off.map(|offset| offset.get());
let runpath = parsed.runpath_off.map(|offset| offset.get());
Ok(Self {
dynamic: ScannedDynamicInfo::new(
parsed.bind_now
|| parsed.flags & DF_BIND_NOW as usize != 0
|| parsed.flags_1 & DF_1_NOW as usize != 0,
parsed.flags & DF_STATIC_TLS as usize != 0,
),
strtab: strtab.into_boxed_slice(),
needed_libs,
soname,
rpath,
runpath,
})
}
}
struct SectionTable<L: ElfLayout = NativeElfLayout> {
sections: Box<[ElfShdr<L>]>,
shstrtab: Box<[u8]>,
}
impl<L: ElfLayout> SectionTable<L> {
fn new(object: &mut dyn ElfReader, ehdr: &ElfHeader<L>) -> Result<Option<Self>> {
if ehdr.e_shnum() == 0 {
return Ok(None);
}
let Some((start, _)) = ehdr.checked_shdr_layout()? else {
return Ok(None);
};
let shdrs = object.read_to_vec::<ElfShdr<L>>(start, ehdr.e_shnum())?;
let shstrndx = ehdr.e_shstrndx();
let shstrtab = match shdrs.get(shstrndx) {
Some(shdr) if shdr.section_type() != ElfSectionType::NOBITS => {
object.read_to_vec(shdr.sh_offset(), shdr.sh_size())?
}
_ => return Ok(None),
};
Ok(Some(SectionTable {
sections: shdrs.into_boxed_slice(),
shstrtab: shstrtab.into_boxed_slice(),
}))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ModuleCapability {
Opaque,
SectionData,
SectionReorderable,
}
impl ModuleCapability {
#[inline]
pub const fn has_section_data(self) -> bool {
!matches!(self, Self::Opaque)
}
#[inline]
pub const fn supports_reorder_repair(self) -> bool {
matches!(self, Self::SectionReorderable)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ScannedDynamicInfo {
bind_now: bool,
static_tls: bool,
}
impl ScannedDynamicInfo {
#[inline]
pub(crate) const fn new(bind_now: bool, static_tls: bool) -> Self {
Self {
bind_now,
static_tls,
}
}
#[inline]
pub fn bind_now(&self) -> bool {
self.bind_now
}
#[inline]
pub fn static_tls(&self) -> bool {
self.static_tls
}
}
pub struct ScannedDynamic<Arch: RelocationArch = NativeArch> {
path: PathBuf,
ehdr: ElfHeader<Arch::Layout>,
phdrs: Box<[ElfPhdr<Arch::Layout>]>,
interp: Option<Box<[u8]>>,
_strtab_bytes: Box<[u8]>,
strtab: ElfStringTable,
section_table: Option<SectionTable<Arch::Layout>>,
soname: Option<usize>,
rpath: Option<usize>,
runpath: Option<usize>,
needed_libs: Box<[usize]>,
capability: ModuleCapability,
reader: Box<dyn ElfReader + 'static>,
dynamic: ScannedDynamicInfo,
}
pub struct ScannedExec<Arch: RelocationArch = NativeArch> {
path: PathBuf,
ehdr: ElfHeader<Arch::Layout>,
phdrs: Box<[ElfPhdr<Arch::Layout>]>,
interp: Option<Box<[u8]>>,
section_table: Option<SectionTable<Arch::Layout>>,
#[allow(dead_code)]
reader: Box<dyn ElfReader + 'static>,
}
#[derive(Debug)]
pub enum ScannedElf<Arch: RelocationArch = NativeArch> {
Dynamic(ScannedDynamic<Arch>),
StaticExec(ScannedExec<Arch>),
}
pub(crate) struct ScannedDynamicLoadParts<Arch: RelocationArch = NativeArch> {
pub(crate) ehdr: ElfHeader<Arch::Layout>,
pub(crate) phdrs: Box<[ElfPhdr<Arch::Layout>]>,
pub(crate) reader: Box<dyn ElfReader + 'static>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct ScannedSectionId(usize);
entity_ref!(ScannedSectionId);
impl ScannedSectionId {
#[inline]
pub const fn from_symbol_shndx(index: ElfSectionIndex) -> Option<Self> {
if index.is_undef() || index.is_abs() {
None
} else {
Some(Self::new(index.index()))
}
}
}
pub struct ScannedSection<'a, L: ElfLayout = NativeElfLayout> {
id: ScannedSectionId,
name: &'a str,
header: &'a ElfShdr<L>,
}
impl<L: ElfLayout> Copy for ScannedSection<'_, L> {}
impl<L: ElfLayout> Clone for ScannedSection<'_, L> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
pub struct ScannedSections<'a, L: ElfLayout = NativeElfLayout> {
sections: &'a [ElfShdr<L>],
shstrtab: *const u8,
index: usize,
}
impl<'a, L: ElfLayout> ScannedSections<'a, L> {
#[inline]
fn new(sections: &'a [ElfShdr<L>], shstrtab: *const u8) -> Self {
Self {
sections,
shstrtab,
index: 0,
}
}
#[inline]
fn section_name(&self, section: &ElfShdr<L>) -> &'a str {
let table = ElfStringTable::new(self.shstrtab);
table.get_str(section.sh_name() as usize)
}
}
impl<'a, L: ElfLayout> Iterator for ScannedSections<'a, L> {
type Item = ScannedSection<'a, L>;
fn next(&mut self) -> Option<Self::Item> {
let header = self.sections.get(self.index)?;
let id = ScannedSectionId::new(self.index);
self.index += 1;
Some(ScannedSection::new(id, self.section_name(header), header))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.sections.len().saturating_sub(self.index);
(remaining, Some(remaining))
}
}
impl<L: ElfLayout> ExactSizeIterator for ScannedSections<'_, L> {}
impl<'a, L: ElfLayout> ScannedSection<'a, L> {
#[inline]
fn new(id: ScannedSectionId, name: &'a str, header: &'a ElfShdr<L>) -> Self {
Self { id, name, header }
}
#[inline]
pub const fn id(&self) -> ScannedSectionId {
self.id
}
#[inline]
pub fn name(&self) -> &'a str {
self.name
}
#[inline]
pub fn header(&self) -> &'a ElfShdr<L> {
self.header
}
#[inline]
pub fn section_type(&self) -> ElfSectionType {
self.header.section_type()
}
#[inline]
pub fn flags(&self) -> ElfSectionFlags {
self.header.flags()
}
#[inline]
pub fn address(&self) -> usize {
self.header.sh_addr()
}
#[inline]
pub fn file_offset(&self) -> usize {
self.header.sh_offset()
}
#[inline]
pub fn size(&self) -> usize {
self.header.sh_size()
}
#[inline]
pub fn alignment(&self) -> usize {
self.header.sh_addralign()
}
#[inline]
pub fn is_allocated(&self) -> bool {
self.flags().contains(ElfSectionFlags::ALLOC)
}
#[inline]
pub fn is_writable(&self) -> bool {
self.flags().contains(ElfSectionFlags::WRITE)
}
#[inline]
pub fn is_executable(&self) -> bool {
self.flags().contains(ElfSectionFlags::EXECINSTR)
}
#[inline]
pub fn is_tls(&self) -> bool {
self.flags().contains(ElfSectionFlags::TLS)
}
#[inline]
pub fn is_nobits(&self) -> bool {
self.section_type() == ElfSectionType::NOBITS
}
#[inline]
pub fn is_relocation_section(&self) -> bool {
matches!(
self.section_type(),
ElfSectionType::REL | ElfSectionType::RELA
)
}
#[inline]
pub fn linked_section_id(&self) -> Option<ScannedSectionId> {
(self.header.sh_link() != 0)
.then_some(ScannedSectionId::new(self.header.sh_link() as usize))
}
#[inline]
pub fn info_section_id(&self) -> Option<ScannedSectionId> {
(self.header.sh_info() != 0)
.then_some(ScannedSectionId::new(self.header.sh_info() as usize))
}
}
impl<'a, L: ElfLayout> fmt::Debug for ScannedSection<'a, L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ScannedSection")
.field("id", &self.id)
.field("name", &self.name)
.field("type", &self.section_type())
.field("size", &self.size())
.field("align", &self.alignment())
.finish()
}
}
impl<Arch: RelocationArch> fmt::Debug for ScannedDynamic<Arch> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ScannedDynamic")
.field("path", &self.path)
.field("soname", &self.soname())
.field("needed_libs", &self.needed_libs().collect::<Vec<_>>())
.field(
"sections",
&self
.section_table
.as_ref()
.map_or(0, |table| table.sections.len()),
)
.field("capability", &self.capability)
.field("bind_now", &self.dynamic.bind_now)
.field("static_tls", &self.dynamic.static_tls)
.finish()
}
}
impl<Arch: RelocationArch> fmt::Debug for ScannedExec<Arch> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ScannedExec")
.field("path", &self.path)
.field(
"sections",
&self
.section_table
.as_ref()
.map_or(0, |table| table.sections.len()),
)
.finish()
}
}
impl<Arch: RelocationArch> ScannedElf<Arch> {
#[inline]
pub fn path(&self) -> &Path {
match self {
Self::Dynamic(image) => image.path(),
Self::StaticExec(image) => image.path(),
}
}
#[inline]
pub fn name(&self) -> &str {
match self {
Self::Dynamic(image) => image.name(),
Self::StaticExec(image) => image.name(),
}
}
#[inline]
pub fn ehdr(&self) -> &ElfHeader<Arch::Layout> {
match self {
Self::Dynamic(image) => image.ehdr(),
Self::StaticExec(image) => image.ehdr(),
}
}
#[inline]
pub fn phdrs(&self) -> &[ElfPhdr<Arch::Layout>] {
match self {
Self::Dynamic(image) => image.phdrs(),
Self::StaticExec(image) => image.phdrs(),
}
}
#[inline]
pub fn interp(&self) -> Option<&str> {
match self {
Self::Dynamic(image) => image.interp(),
Self::StaticExec(image) => image.interp(),
}
}
#[inline]
pub fn as_dynamic(&self) -> Option<&ScannedDynamic<Arch>> {
match self {
Self::Dynamic(image) => Some(image),
Self::StaticExec(_) => None,
}
}
#[inline]
pub fn as_static_exec(&self) -> Option<&ScannedExec<Arch>> {
match self {
Self::Dynamic(_) => None,
Self::StaticExec(image) => Some(image),
}
}
#[inline]
pub fn into_dynamic(self) -> Option<ScannedDynamic<Arch>> {
match self {
Self::Dynamic(image) => Some(image),
Self::StaticExec(_) => None,
}
}
#[inline]
pub fn into_static_exec(self) -> Option<ScannedExec<Arch>> {
match self {
Self::Dynamic(_) => None,
Self::StaticExec(image) => Some(image),
}
}
#[inline]
pub fn has_sections(&self) -> bool {
match self {
Self::Dynamic(image) => image.has_sections(),
Self::StaticExec(image) => image.has_sections(),
}
}
#[inline]
pub fn section_headers(&self) -> Option<&[ElfShdr<Arch::Layout>]> {
match self {
Self::Dynamic(image) => image.section_headers(),
Self::StaticExec(image) => image.section_headers(),
}
}
#[inline]
pub fn section(
&self,
id: impl Into<ScannedSectionId>,
) -> Option<ScannedSection<'_, Arch::Layout>> {
match self {
Self::Dynamic(image) => image.section(id),
Self::StaticExec(image) => image.section(id),
}
}
#[inline]
pub fn sections(&self) -> ScannedSections<'_, Arch::Layout> {
match self {
Self::Dynamic(image) => image.sections(),
Self::StaticExec(image) => image.sections(),
}
}
#[inline]
pub fn alloc_sections(&self) -> impl Iterator<Item = ScannedSection<'_, Arch::Layout>> {
self.sections().filter(|section| section.is_allocated())
}
}
impl<Arch: RelocationArch> ScannedDynamic<Arch> {
pub(crate) fn from_builder(builder: ScanBuilder<Arch::Layout>) -> Result<Self> {
let ScanBuilder {
path,
ehdr,
phdrs,
mut reader,
} = builder;
let interp = read_interp(reader.as_mut(), &phdrs)?;
let DynamicScanParts {
dynamic,
strtab,
needed_libs,
soname,
rpath,
runpath,
} = DynamicScanParts::new(reader.as_mut(), &phdrs)?;
let section_table = SectionTable::new(reader.as_mut(), &ehdr)?;
let strtab_view = ElfStringTable::new(strtab.as_ptr());
let capability = section_table
.as_ref()
.map_or(ModuleCapability::Opaque, |table| {
classify_module_capability(&table.sections)
});
Ok(Self {
path,
ehdr,
phdrs,
interp,
_strtab_bytes: strtab,
strtab: strtab_view,
section_table,
soname,
rpath,
runpath,
needed_libs,
capability,
reader,
dynamic,
})
}
#[inline]
pub fn path(&self) -> &Path {
self.path.as_path()
}
#[inline]
pub fn name(&self) -> &str {
self.soname().unwrap_or_else(|| self.path().file_name())
}
#[inline]
pub fn ehdr(&self) -> &ElfHeader<Arch::Layout> {
&self.ehdr
}
#[inline]
pub fn phdrs(&self) -> &[ElfPhdr<Arch::Layout>] {
&self.phdrs
}
#[inline]
pub fn interp(&self) -> Option<&str> {
self.interp.as_deref().and_then(|bytes| {
let end = bytes
.iter()
.position(|byte| *byte == 0)
.unwrap_or(bytes.len());
core::str::from_utf8(&bytes[..end]).ok()
})
}
#[inline]
pub fn rpath(&self) -> Option<&str> {
self.rpath.map(|offset| self.strtab.get_str(offset))
}
#[inline]
pub fn runpath(&self) -> Option<&str> {
self.runpath.map(|offset| self.strtab.get_str(offset))
}
#[inline]
pub fn soname(&self) -> Option<&str> {
self.soname.map(|offset| self.strtab.get_str(offset))
}
#[inline]
pub fn needed_lib(&self, index: usize) -> Option<&str> {
self.needed_libs
.get(index)
.map(|offset| self.strtab.get_str(*offset))
}
#[inline]
pub fn needed_libs(&self) -> impl ExactSizeIterator<Item = &str> + '_ {
self.needed_libs
.iter()
.map(|offset| self.strtab.get_str(*offset))
}
#[inline]
pub const fn capability(&self) -> ModuleCapability {
self.capability
}
#[inline]
fn shstrtab(&self) -> Option<ElfStringTable> {
self.section_table
.as_ref()
.map(|table| ElfStringTable::new(table.shstrtab.as_ptr()))
}
#[inline]
pub fn has_sections(&self) -> bool {
self.section_table.is_some()
}
#[inline]
pub fn section_headers(&self) -> Option<&[ElfShdr<Arch::Layout>]> {
self.section_table
.as_ref()
.map(|table| table.sections.as_ref())
}
#[inline]
pub fn section(
&self,
id: impl Into<ScannedSectionId>,
) -> Option<ScannedSection<'_, Arch::Layout>> {
let id = id.into();
let section_table = self.section_table.as_ref()?;
let shstrtab = self.shstrtab()?;
let header = section_table.sections.get(id.index())?;
Some(ScannedSection::new(
id,
shstrtab.get_str(header.sh_name() as usize),
header,
))
}
#[inline]
pub fn sections(&self) -> ScannedSections<'_, Arch::Layout> {
ScannedSections::new(
self.section_table
.as_ref()
.map_or(&[], |table| table.sections.as_ref()),
self.section_table
.as_ref()
.map_or(ptr::null(), |table| table.shstrtab.as_ptr()),
)
}
#[inline]
pub fn alloc_sections(&self) -> impl Iterator<Item = ScannedSection<'_, Arch::Layout>> {
self.sections().filter(|section| section.is_allocated())
}
#[inline]
pub fn relocation_sections(&self) -> impl Iterator<Item = ScannedSection<'_, Arch::Layout>> {
self.sections()
.filter(|section| section.is_relocation_section())
}
pub(crate) fn section_data(
&mut self,
id: impl Into<ScannedSectionId>,
) -> Result<Option<AlignedBytes>> {
let Some(section) = self.section(id) else {
return Ok(None);
};
if section.is_nobits() {
return Ok(Some(
AlignedBytes::with_len(section.size()).expect("failed to allocate section bytes"),
));
}
Ok(Some(
self.read_bytes(section.file_offset(), section.size())?,
))
}
#[inline]
fn read_bytes(&mut self, offset: usize, len: usize) -> Result<AlignedBytes> {
let mut bytes = AlignedBytes::with_len(len).ok_or(ParseDynamicError::AddressOverflow)?;
self.reader.read_slice(bytes.as_mut(), offset)?;
Ok(bytes)
}
#[inline]
pub fn dynamic(&self) -> &ScannedDynamicInfo {
&self.dynamic
}
pub(crate) fn into_load_parts(self) -> ScannedDynamicLoadParts<Arch> {
let Self {
ehdr,
phdrs,
reader,
..
} = self;
ScannedDynamicLoadParts {
ehdr,
phdrs,
reader,
}
}
}
impl<Arch: RelocationArch> ScannedExec<Arch> {
pub(crate) fn from_builder(builder: ScanBuilder<Arch::Layout>) -> Result<Self> {
let ScanBuilder {
path,
ehdr,
phdrs,
mut reader,
} = builder;
let interp = read_interp(reader.as_mut(), &phdrs)?;
let section_table = SectionTable::new(reader.as_mut(), &ehdr)?;
Ok(Self {
path,
ehdr,
phdrs,
interp,
section_table,
reader,
})
}
#[inline]
pub fn path(&self) -> &Path {
self.path.as_path()
}
#[inline]
pub fn name(&self) -> &str {
self.path().file_name()
}
#[inline]
pub fn ehdr(&self) -> &ElfHeader<Arch::Layout> {
&self.ehdr
}
#[inline]
pub fn phdrs(&self) -> &[ElfPhdr<Arch::Layout>] {
&self.phdrs
}
#[inline]
pub fn interp(&self) -> Option<&str> {
self.interp.as_deref().and_then(|bytes| {
let end = bytes
.iter()
.position(|byte| *byte == 0)
.unwrap_or(bytes.len());
core::str::from_utf8(&bytes[..end]).ok()
})
}
#[inline]
fn shstrtab(&self) -> Option<ElfStringTable> {
self.section_table
.as_ref()
.map(|table| ElfStringTable::new(table.shstrtab.as_ptr()))
}
#[inline]
pub fn has_sections(&self) -> bool {
self.section_table.is_some()
}
#[inline]
pub fn section_headers(&self) -> Option<&[ElfShdr<Arch::Layout>]> {
self.section_table
.as_ref()
.map(|table| table.sections.as_ref())
}
#[inline]
pub fn section(
&self,
id: impl Into<ScannedSectionId>,
) -> Option<ScannedSection<'_, Arch::Layout>> {
let id = id.into();
let section_table = self.section_table.as_ref()?;
let shstrtab = self.shstrtab()?;
let header = section_table.sections.get(id.index())?;
Some(ScannedSection::new(
id,
shstrtab.get_str(header.sh_name() as usize),
header,
))
}
#[inline]
pub fn sections(&self) -> ScannedSections<'_, Arch::Layout> {
ScannedSections::new(
self.section_table
.as_ref()
.map_or(&[], |table| table.sections.as_ref()),
self.section_table
.as_ref()
.map_or(ptr::null(), |table| table.shstrtab.as_ptr()),
)
}
#[inline]
pub fn alloc_sections(&self) -> impl Iterator<Item = ScannedSection<'_, Arch::Layout>> {
self.sections().filter(|section| section.is_allocated())
}
#[allow(dead_code)]
pub(crate) fn section_data(
&mut self,
id: impl Into<ScannedSectionId>,
) -> Result<Option<AlignedBytes>> {
let Some(section) = self.section(id) else {
return Ok(None);
};
if section.is_nobits() {
return Ok(Some(
AlignedBytes::with_len(section.size()).expect("failed to allocate section bytes"),
));
}
Ok(Some(
self.read_bytes(section.file_offset(), section.size())?,
))
}
#[inline]
#[allow(dead_code)]
fn read_bytes(&mut self, offset: usize, len: usize) -> Result<AlignedBytes> {
let mut bytes = AlignedBytes::with_len(len).ok_or(ParseDynamicError::AddressOverflow)?;
self.reader.read_slice(bytes.as_mut(), offset)?;
Ok(bytes)
}
}
fn classify_module_capability<L: ElfLayout>(sections: &[ElfShdr<L>]) -> ModuleCapability {
for section in sections {
if !matches!(
section.section_type(),
ElfSectionType::REL | ElfSectionType::RELA
) {
continue;
}
if !section.flags().contains(ElfSectionFlags::ALLOC)
&& section.sh_info() != 0
&& section.sh_link() != 0
{
return ModuleCapability::SectionReorderable;
}
}
ModuleCapability::SectionData
}
fn read_interp<L: ElfLayout>(
object: &mut dyn ElfReader,
phdrs: &[ElfPhdr<L>],
) -> Result<Option<Box<[u8]>>> {
let Some(interp) = phdrs
.iter()
.find(|phdr| phdr.program_type() == ElfProgramType::INTERP)
else {
return Ok(None);
};
let bytes = object.read_to_vec(interp.p_offset(), interp.p_filesz())?;
Ok(Some(bytes.into_boxed_slice()))
}
fn vaddr_to_file_offset<L: ElfLayout>(vaddr: usize, phdrs: &[ElfPhdr<L>]) -> Result<usize> {
for phdr in phdrs
.iter()
.filter(|phdr| phdr.program_type() == ElfProgramType::LOAD)
{
let seg_start = phdr.p_vaddr();
let seg_end = seg_start
.checked_add(phdr.p_filesz())
.ok_or(ParseDynamicError::AddressOverflow)?;
if seg_start <= vaddr && vaddr < seg_end {
return phdr
.p_offset()
.checked_add(vaddr - seg_start)
.ok_or(ParseDynamicError::AddressOverflow.into());
}
}
Err(ParsePhdrError::malformed("virtual address is not covered by a PT_LOAD segment").into())
}