use core::marker::PhantomData;
use goblin::mach::load_command::{
CommandVariant, DylibCommand, LoadCommand as GoblinLoadCommand, cmd_to_str,
};
use crate::binary::{Version, read_lc_str};
#[derive(Debug)]
pub struct Dylib<'a, 'p> {
pub name: &'a str,
pub kind: DylibKind,
pub timestamp: u32,
pub current_version: Version,
pub compat_version: Version,
_parent: PhantomData<&'p ()>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DylibKind {
Load,
LoadWeak,
Reexport,
LoadUpward,
LazyLoad,
}
pub struct DylibIter<'a, 'p> {
data: &'a [u8],
inner: core::slice::Iter<'p, GoblinLoadCommand>,
}
impl<'a, 'p> DylibIter<'a, 'p> {
pub(crate) fn new(data: &'a [u8], lcs: &'p [GoblinLoadCommand]) -> Self {
Self {
data,
inner: lcs.iter(),
}
}
}
impl<'a, 'p> Iterator for DylibIter<'a, 'p> {
type Item = Dylib<'a, 'p>;
fn next(&mut self) -> Option<Self::Item> {
for lc in self.inner.by_ref() {
let (kind, c): (DylibKind, &DylibCommand) = match &lc.command {
CommandVariant::LoadDylib(c) => (DylibKind::Load, c),
CommandVariant::LoadWeakDylib(c) => (DylibKind::LoadWeak, c),
CommandVariant::ReexportDylib(c) => (DylibKind::Reexport, c),
CommandVariant::LoadUpwardDylib(c) => (DylibKind::LoadUpward, c),
CommandVariant::LazyLoadDylib(c) => (DylibKind::LazyLoad, c),
_ => continue,
};
let Some(name) = resolve_dylib_name(self.data, lc, c) else {
continue;
};
return Some(Dylib {
name,
kind,
timestamp: c.dylib.timestamp,
current_version: Version::from_packed_u32(c.dylib.current_version),
compat_version: Version::from_packed_u32(c.dylib.compatibility_version),
_parent: PhantomData,
});
}
None
}
}
fn resolve_dylib_name<'a>(
data: &'a [u8],
lc: &GoblinLoadCommand,
c: &DylibCommand,
) -> Option<&'a str> {
let name_off = c.dylib.name as usize;
let total_off = lc.offset.checked_add(name_off)?;
let end = lc.offset.checked_add(lc.command.cmdsize())?;
read_lc_str(data, total_off, end)
}
#[derive(Debug)]
pub struct LoadCommand<'a, 'p> {
pub kind: u32,
pub offset: usize,
pub size: u32,
pub bytes: &'a [u8],
_parent: PhantomData<&'p ()>,
}
impl<'a, 'p> LoadCommand<'a, 'p> {
pub fn name(&self) -> &'static str {
cmd_to_str(self.kind)
}
}
pub struct LoadCommandIter<'a, 'p> {
data: &'a [u8],
inner: core::slice::Iter<'p, GoblinLoadCommand>,
}
impl<'a, 'p> LoadCommandIter<'a, 'p> {
pub(crate) fn new(data: &'a [u8], lcs: &'p [GoblinLoadCommand]) -> Self {
Self {
data,
inner: lcs.iter(),
}
}
}
impl<'a, 'p> Iterator for LoadCommandIter<'a, 'p> {
type Item = LoadCommand<'a, 'p>;
fn next(&mut self) -> Option<Self::Item> {
let lc = self.inner.next()?;
let kind = lc.command.cmd();
let cmdsize = lc.command.cmdsize();
let bytes = lc
.offset
.checked_add(cmdsize)
.and_then(|end| self.data.get(lc.offset..end))
.unwrap_or(&[]);
Some(LoadCommand {
kind,
offset: lc.offset,
size: cmdsize as u32,
bytes,
_parent: PhantomData,
})
}
}