use std::marker::PhantomData;
use crate::objc::{
ObjcRuntime, class::decode_class_name, protocol::decode_protocol, strip_objc_symbol_prefix,
};
#[derive(Debug, Clone, Copy)]
pub enum RefTarget<'a> {
Local {
address: u64,
name: Option<&'a str>,
},
External {
name: &'a str,
dylib: &'a str,
},
Unresolved {
slot_address: u64,
target: u64,
},
}
pub struct SelRefIter<'a, 'p> {
rt: &'p ObjcRuntime<'a>,
cursor: usize,
_phantom: PhantomData<&'a ()>,
}
impl<'a, 'p> SelRefIter<'a, 'p> {
pub(crate) fn new(rt: &'p ObjcRuntime<'a>) -> Self {
Self {
rt,
cursor: 0,
_phantom: PhantomData,
}
}
}
impl<'a, 'p> Iterator for SelRefIter<'a, 'p> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
let sec = self.rt.sel_refs?;
loop {
if self.cursor.checked_add(8)? > sec.body.len() {
return None;
}
let slot_va = sec.vmaddr.wrapping_add(self.cursor as u64);
self.cursor = self.cursor.checked_add(8)?;
let sel_va = self.rt.resolve_pointer(slot_va).unwrap_or(0);
if sel_va == 0 {
continue;
}
if let Some(s) = self.rt.read_cstr(sel_va) {
return Some(s);
}
}
}
}
fn next_ref_target<'a>(
rt: &ObjcRuntime<'a>,
sec_body: &[u8],
sec_vmaddr: u64,
cursor: &mut usize,
resolve_local: impl Fn(&ObjcRuntime<'a>, u64) -> Option<&'a str>,
) -> Option<RefTarget<'a>> {
if cursor.checked_add(8)? > sec_body.len() {
return None;
}
let slot_offset = *cursor as u64;
*cursor = cursor.checked_add(8)?;
let slot_va = sec_vmaddr.wrapping_add(slot_offset);
let target = rt.resolve_pointer(slot_va).unwrap_or(0);
if target != 0 {
if let Some(name) = resolve_local(rt, target) {
return Some(RefTarget::Local {
address: target,
name: Some(name),
});
}
if !rt.binds_by_va.contains_key(&slot_va) && rt.vm_to_file_offset(target).is_some() {
return Some(RefTarget::Local {
address: target,
name: None,
});
}
}
if let Some((sym, dylib)) = rt.binds_by_va.get(&slot_va) {
return Some(RefTarget::External {
name: strip_objc_symbol_prefix(sym),
dylib,
});
}
Some(RefTarget::Unresolved {
slot_address: slot_va,
target,
})
}
pub struct ClassRefIter<'a, 'p> {
rt: &'p ObjcRuntime<'a>,
cursor: usize,
_phantom: PhantomData<&'a ()>,
}
impl<'a, 'p> ClassRefIter<'a, 'p> {
pub(crate) fn new(rt: &'p ObjcRuntime<'a>) -> Self {
Self {
rt,
cursor: 0,
_phantom: PhantomData,
}
}
}
impl<'a, 'p> Iterator for ClassRefIter<'a, 'p> {
type Item = RefTarget<'a>;
fn next(&mut self) -> Option<Self::Item> {
let sec = self.rt.class_refs?;
next_ref_target(self.rt, sec.body, sec.vmaddr, &mut self.cursor, |rt, va| {
decode_class_name(rt, va)
})
}
}
pub struct SuperRefIter<'a, 'p> {
rt: &'p ObjcRuntime<'a>,
cursor: usize,
_phantom: PhantomData<&'a ()>,
}
impl<'a, 'p> SuperRefIter<'a, 'p> {
pub(crate) fn new(rt: &'p ObjcRuntime<'a>) -> Self {
Self {
rt,
cursor: 0,
_phantom: PhantomData,
}
}
}
impl<'a, 'p> Iterator for SuperRefIter<'a, 'p> {
type Item = RefTarget<'a>;
fn next(&mut self) -> Option<Self::Item> {
let sec = self.rt.super_refs?;
next_ref_target(self.rt, sec.body, sec.vmaddr, &mut self.cursor, |rt, va| {
decode_class_name(rt, va)
})
}
}
pub struct ProtoRefIter<'a, 'p> {
rt: &'p ObjcRuntime<'a>,
cursor: usize,
_phantom: PhantomData<&'a ()>,
}
impl<'a, 'p> ProtoRefIter<'a, 'p> {
pub(crate) fn new(rt: &'p ObjcRuntime<'a>) -> Self {
Self {
rt,
cursor: 0,
_phantom: PhantomData,
}
}
}
impl<'a, 'p> Iterator for ProtoRefIter<'a, 'p> {
type Item = RefTarget<'a>;
fn next(&mut self) -> Option<Self::Item> {
let sec = self.rt.proto_refs?;
next_ref_target(self.rt, sec.body, sec.vmaddr, &mut self.cursor, |rt, va| {
decode_protocol(rt, va).map(|p| p.name())
})
}
}