use crate::{
swift::{
SwiftRuntime,
context::{ConformanceFlags, TypeReferenceKind},
},
util::{read_i32_le_at, read_u32_le_at, relative_pointer},
};
#[derive(Debug, Clone)]
pub enum TypeReference<'a> {
DirectTypeDescriptor(u64),
IndirectTypeDescriptor(u64),
DirectObjCClassName(&'a str),
IndirectObjCClass(u64),
Other {
kind: u8,
target: u64,
},
}
#[derive(Debug)]
pub struct Conformance<'a, 'p> {
pub(crate) rt: &'p SwiftRuntime<'a>,
pub(crate) address: u64,
pub(crate) protocol_descriptor_va: u64,
pub(crate) type_ref: TypeReference<'a>,
pub(crate) witness_table_va: u64,
pub(crate) flags: ConformanceFlags,
}
impl<'a, 'p> Conformance<'a, 'p> {
pub fn runtime(&self) -> &'p SwiftRuntime<'a> {
self.rt
}
pub fn address(&self) -> u64 {
self.address
}
pub fn protocol_descriptor_address(&self) -> u64 {
self.protocol_descriptor_va
}
pub fn type_ref(&self) -> &TypeReference<'a> {
&self.type_ref
}
pub fn witness_table_address(&self) -> u64 {
self.witness_table_va
}
pub fn flags(&self) -> ConformanceFlags {
self.flags
}
}
pub struct ConformanceIter<'a, 'p> {
rt: &'p SwiftRuntime<'a>,
cursor: usize,
}
impl<'a, 'p> ConformanceIter<'a, 'p> {
pub(crate) fn new(rt: &'p SwiftRuntime<'a>) -> Self {
Self { rt, cursor: 0 }
}
}
impl<'a, 'p> Iterator for ConformanceIter<'a, 'p> {
type Item = Conformance<'a, 'p>;
fn next(&mut self) -> Option<Self::Item> {
let section = self.rt.proto.as_ref()?;
loop {
let slot_off = self.cursor;
let slot_end = slot_off.checked_add(4)?;
if slot_end > section.body.len() {
return None;
}
self.cursor = slot_end;
let Some(rel) = read_i32_le_at(section.body, slot_off) else {
continue;
};
if rel == 0 {
continue;
}
let slot_va = section.vmaddr.wrapping_add(slot_off as u64);
let descriptor_va = relative_pointer(slot_va, rel);
if let Some(c) = decode_conformance(self.rt, descriptor_va) {
return Some(c);
}
#[cfg(feature = "tracing")]
tracing::debug!(
"darwinscope::swift: conformance descriptor at 0x{:x} (slot 0x{:x}) skipped",
descriptor_va,
slot_va,
);
}
}
}
fn decode_conformance<'a, 'p>(
rt: &'p SwiftRuntime<'a>,
descriptor_va: u64,
) -> Option<Conformance<'a, 'p>> {
let header = rt.read_bytes(descriptor_va, 16)?;
let protocol_rel = read_i32_le_at(header, 0)?;
let type_ref_rel = read_i32_le_at(header, 4)?;
let witness_rel = read_i32_le_at(header, 8)?;
let flags_raw = read_u32_le_at(header, 12)?;
let flags = ConformanceFlags(flags_raw);
let protocol_slot = descriptor_va;
let protocol_descriptor_va = if protocol_rel == 0 {
0
} else {
relative_pointer(protocol_slot, protocol_rel)
};
let type_ref_slot = descriptor_va.checked_add(4)?;
let type_ref =
decode_type_reference(rt, type_ref_slot, type_ref_rel, flags.type_reference_kind());
let witness_slot = descriptor_va.checked_add(8)?;
let witness_table_va = if witness_rel == 0 {
0
} else {
relative_pointer(witness_slot, witness_rel)
};
Some(Conformance {
rt,
address: descriptor_va,
protocol_descriptor_va,
type_ref,
witness_table_va,
flags,
})
}
fn decode_type_reference<'a>(
rt: &SwiftRuntime<'a>,
slot_va: u64,
rel: i32,
kind: TypeReferenceKind,
) -> TypeReference<'a> {
let target = if rel == 0 {
0
} else {
relative_pointer(slot_va, rel)
};
match kind {
TypeReferenceKind::DirectTypeDescriptor => TypeReference::DirectTypeDescriptor(target),
TypeReferenceKind::IndirectTypeDescriptor => TypeReference::IndirectTypeDescriptor(target),
TypeReferenceKind::DirectObjCClassName => {
match rt.read_cstr(target) {
Some(name) => TypeReference::DirectObjCClassName(name),
None => TypeReference::Other { kind: 2, target },
}
}
TypeReferenceKind::IndirectObjCClass => TypeReference::IndirectObjCClass(target),
TypeReferenceKind::Other(k) => TypeReference::Other { kind: k, target },
}
}