use crate::{
swift::{SwiftRuntime, context::ContextDescriptorFlags},
util::{read_i32_le_at, read_u32_le_at, relative_pointer},
};
#[derive(Debug)]
pub struct SwiftProtocol<'a, 'p> {
pub(crate) rt: &'p SwiftRuntime<'a>,
pub(crate) address: u64,
pub(crate) flags: ContextDescriptorFlags,
pub(crate) parent_va: u64,
pub(crate) name: &'a str,
pub(crate) num_requirements_in_signature: u32,
pub(crate) num_requirements: u32,
pub(crate) associated_type_names: Option<&'a str>,
}
impl<'a, 'p> SwiftProtocol<'a, 'p> {
pub fn runtime(&self) -> &'p SwiftRuntime<'a> {
self.rt
}
pub fn address(&self) -> u64 {
self.address
}
pub fn flags(&self) -> ContextDescriptorFlags {
self.flags
}
pub fn parent_address(&self) -> u64 {
self.parent_va
}
pub fn name(&self) -> &'a str {
self.name
}
pub fn num_requirements_in_signature(&self) -> u32 {
self.num_requirements_in_signature
}
pub fn num_requirements(&self) -> u32 {
self.num_requirements
}
pub fn associated_type_names(&self) -> Option<&'a str> {
self.associated_type_names
}
}
pub struct ProtocolIter<'a, 'p> {
rt: &'p SwiftRuntime<'a>,
cursor: usize,
}
impl<'a, 'p> ProtocolIter<'a, 'p> {
pub(crate) fn new(rt: &'p SwiftRuntime<'a>) -> Self {
Self { rt, cursor: 0 }
}
}
impl<'a, 'p> Iterator for ProtocolIter<'a, 'p> {
type Item = SwiftProtocol<'a, 'p>;
fn next(&mut self) -> Option<Self::Item> {
let section = self.rt.protos.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(p) = decode_protocol(self.rt, descriptor_va) {
return Some(p);
}
#[cfg(feature = "tracing")]
tracing::debug!(
"darwinscope::swift: protocol descriptor at 0x{:x} (slot 0x{:x}) skipped",
descriptor_va,
slot_va,
);
}
}
}
fn decode_protocol<'a, 'p>(
rt: &'p SwiftRuntime<'a>,
descriptor_va: u64,
) -> Option<SwiftProtocol<'a, 'p>> {
let header = rt.read_bytes(descriptor_va, 24)?;
let flags_raw = read_u32_le_at(header, 0)?;
let parent_rel = read_i32_le_at(header, 4)?;
let name_rel = read_i32_le_at(header, 8)?;
let num_requirements_in_signature = read_u32_le_at(header, 12)?;
let num_requirements = read_u32_le_at(header, 16)?;
let associated_rel = read_i32_le_at(header, 20)?;
let flags = ContextDescriptorFlags(flags_raw);
let parent_slot_va = descriptor_va.checked_add(4)?;
let parent_va = if parent_rel == 0 {
0
} else {
relative_pointer(parent_slot_va, parent_rel)
};
let name_slot_va = descriptor_va.checked_add(8)?;
let name_va = relative_pointer(name_slot_va, name_rel);
let name = rt.read_cstr(name_va)?;
let associated_type_names = if associated_rel == 0 {
None
} else {
let assoc_slot = descriptor_va.checked_add(20)?;
let assoc_va = relative_pointer(assoc_slot, associated_rel);
rt.read_cstr(assoc_va)
};
Some(SwiftProtocol {
rt,
address: descriptor_va,
flags,
parent_va,
name,
num_requirements_in_signature,
num_requirements,
associated_type_names,
})
}