use crate::{
swift::{
SwiftRuntime,
context::{FieldDescriptorKind, FieldRecordFlags},
reflstr,
},
util::{read_i32_le_at, read_u16_le_at, read_u32_le_at, relative_pointer},
};
const FIELD_DESCRIPTOR_HEADER_SIZE: u64 = 16;
#[derive(Debug)]
pub struct FieldDescriptor<'a, 'p> {
pub(crate) rt: &'p SwiftRuntime<'a>,
pub(crate) address: u64,
pub(crate) mangled_type_name: Option<&'a str>,
pub(crate) superclass_mangled_name: Option<&'a str>,
pub(crate) kind: FieldDescriptorKind,
pub(crate) field_record_size: u16,
pub(crate) num_fields: u32,
}
impl<'a, 'p> FieldDescriptor<'a, 'p> {
pub fn address(&self) -> u64 {
self.address
}
pub fn mangled_type_name(&self) -> Option<&'a str> {
self.mangled_type_name
}
pub fn superclass_mangled_name(&self) -> Option<&'a str> {
self.superclass_mangled_name
}
pub fn kind(&self) -> FieldDescriptorKind {
self.kind
}
pub fn field_record_size(&self) -> u16 {
self.field_record_size
}
pub fn num_fields(&self) -> u32 {
self.num_fields
}
pub fn records(&self) -> FieldRecordIter<'a, 'p> {
let base_va = self
.address
.checked_add(FIELD_DESCRIPTOR_HEADER_SIZE)
.unwrap_or(0);
FieldRecordIter {
rt: self.rt,
base_va,
count: self.num_fields,
entry_size: u32::from(self.field_record_size),
cursor: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct FieldRecord<'a> {
pub(crate) flags: FieldRecordFlags,
pub(crate) mangled_type_name: Option<&'a str>,
pub(crate) field_name: Option<&'a str>,
}
impl<'a> FieldRecord<'a> {
pub fn flags(&self) -> FieldRecordFlags {
self.flags
}
pub fn mangled_type_name(&self) -> Option<&'a str> {
self.mangled_type_name
}
pub fn field_name(&self) -> Option<&'a str> {
self.field_name
}
}
pub struct FieldIter<'a, 'p> {
rt: &'p SwiftRuntime<'a>,
cursor: usize,
}
impl<'a, 'p> FieldIter<'a, 'p> {
pub(crate) fn new(rt: &'p SwiftRuntime<'a>) -> Self {
Self { rt, cursor: 0 }
}
}
impl<'a, 'p> Iterator for FieldIter<'a, 'p> {
type Item = FieldDescriptor<'a, 'p>;
fn next(&mut self) -> Option<Self::Item> {
let section = self.rt.fieldmd.as_ref()?;
let start_off = self.cursor;
let header_end = start_off.checked_add(FIELD_DESCRIPTOR_HEADER_SIZE as usize)?;
if header_end > section.body.len() {
return None;
}
let header = section.body.get(start_off..header_end)?;
let mangled_rel = read_i32_le_at(header, 0).unwrap_or(0);
let superclass_rel = read_i32_le_at(header, 4).unwrap_or(0);
let kind_raw = read_u16_le_at(header, 8).unwrap_or(0);
let field_record_size = read_u16_le_at(header, 10).unwrap_or(12);
let num_fields = read_u32_le_at(header, 12).unwrap_or(0);
let body_size = u64::from(num_fields).checked_mul(u64::from(field_record_size));
let total = body_size
.and_then(|b| b.checked_add(FIELD_DESCRIPTOR_HEADER_SIZE))
.and_then(|t| usize::try_from(t).ok());
let next_off = total.and_then(|t| start_off.checked_add(t));
self.cursor = match next_off {
Some(off) if off <= section.body.len() => off,
Some(_) | None => {
#[cfg(feature = "tracing")]
tracing::debug!(
"darwinscope::swift: field descriptor at section+0x{:x} overruns __swift5_fieldmd - stop",
start_off,
);
return None;
}
};
let descriptor_va = section.vmaddr.wrapping_add(start_off as u64);
let mangled_slot_va = descriptor_va;
let mangled_type_name = if mangled_rel == 0 {
None
} else {
self.rt
.read_cstr(relative_pointer(mangled_slot_va, mangled_rel))
};
let superclass_slot_va = descriptor_va.checked_add(4)?;
let superclass_mangled_name = if superclass_rel == 0 {
None
} else {
self.rt
.read_cstr(relative_pointer(superclass_slot_va, superclass_rel))
};
Some(FieldDescriptor {
rt: self.rt,
address: descriptor_va,
mangled_type_name,
superclass_mangled_name,
kind: FieldDescriptorKind::from_bits(kind_raw),
field_record_size,
num_fields,
})
}
}
pub struct FieldRecordIter<'a, 'p> {
rt: &'p SwiftRuntime<'a>,
base_va: u64,
count: u32,
entry_size: u32,
cursor: u32,
}
impl<'a, 'p> Iterator for FieldRecordIter<'a, 'p> {
type Item = FieldRecord<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor >= self.count {
return None;
}
let i = self.cursor;
self.cursor = self.cursor.checked_add(1)?;
let entry_off = u64::from(i).checked_mul(u64::from(self.entry_size))?;
let entry_va = self.base_va.checked_add(entry_off)?;
let bytes = self.rt.read_bytes(entry_va, 12)?;
let flags_raw = read_u32_le_at(bytes, 0)?;
let mangled_rel = read_i32_le_at(bytes, 4)?;
let name_rel = read_i32_le_at(bytes, 8)?;
let flags = FieldRecordFlags(flags_raw);
let mangled_slot_va = entry_va.checked_add(4)?;
let mangled_type_name = if mangled_rel == 0 {
None
} else {
self.rt
.read_cstr(relative_pointer(mangled_slot_va, mangled_rel))
};
let name_slot_va = entry_va.checked_add(8)?;
let field_name = if name_rel == 0 {
None
} else {
reflstr::lookup_field_name(self.rt, relative_pointer(name_slot_va, name_rel))
};
Some(FieldRecord {
flags,
mangled_type_name,
field_name,
})
}
}