use crate::{
swift::{
SwiftRuntime,
context::{ContextDescriptorFlags, MetadataInitializationKind},
typedescriptor::{
ClassBody, DefaultOverrideTableHeader, OverrideTableHeader, VTableHeader,
},
},
util::{read_i32_le_at, read_u16_le_at, read_u32_le_at, relative_pointer},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GenericContextHeader<'a> {
pub address: u64,
pub num_params: u16,
pub num_requirements: u16,
pub num_key_arguments: u16,
pub num_extra_arguments: u16,
pub instantiation_cache_va: u64,
pub default_instantiation_pattern_va: u64,
pub params_base_va: u64,
pub requirements_base_va: u64,
pub _marker: core::marker::PhantomData<&'a [u8]>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResilientSuperclass<'a> {
pub address: u64,
pub superclass_va: u64,
pub _marker: core::marker::PhantomData<&'a [u8]>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ForeignMetadataInit {
pub address: u64,
pub completion_function_va: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SingletonMetadataInit {
pub address: u64,
pub initialization_cache_va: u64,
pub incomplete_metadata_va: u64,
pub completion_function_va: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SingletonMetadataPointer {
pub address: u64,
pub metadata_va: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ObjcResilientClassStubInfo {
pub address: u64,
pub stub_va: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvertibleProtocolSet {
pub address: u64,
pub bits: u16,
}
pub struct PrespecializationIter<'a, 'p> {
pub(crate) rt: &'p SwiftRuntime<'a>,
pub(crate) base_va: u64,
pub(crate) count: u32,
pub(crate) cursor: u32,
}
impl<'a, 'p> PrespecializationIter<'a, 'p> {
#[allow(dead_code)] pub(crate) fn empty(rt: &'p SwiftRuntime<'a>) -> Self {
Self {
rt,
base_va: 0,
count: 0,
cursor: 0,
}
}
}
impl<'a, 'p> Iterator for PrespecializationIter<'a, 'p> {
type Item = u64;
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(4)?;
let slot_va = self.base_va.checked_add(entry_off)?;
let bytes = self.rt.read_bytes(slot_va, 4)?;
let rel = read_i32_le_at(bytes, 0)?;
if rel == 0 {
return Some(0);
}
Some(relative_pointer(slot_va, rel))
}
}
pub(crate) fn decode_class_trailers<'a>(
rt: &SwiftRuntime<'a>,
flags: &ContextDescriptorFlags,
trailing_start: u64,
body: &mut ClassBody<'a>,
) {
let type_flags = flags.type_flags();
let mut cursor = trailing_start;
if flags.is_generic() {
if let Some((header_va, end)) = decode_generic_header(rt, cursor) {
body.generic_header_va = Some(header_va);
cursor = end;
} else {
return;
}
}
if type_flags.class_has_resilient_superclass() {
body.resilient_superclass_va = Some(cursor);
cursor = match cursor.checked_add(4) {
Some(c) => c,
None => return,
};
}
match type_flags.metadata_initialization() {
MetadataInitializationKind::Foreign => {
body.foreign_metadata_init_va = Some(cursor);
cursor = match cursor.checked_add(4) {
Some(c) => c,
None => return,
};
}
MetadataInitializationKind::Singleton => {
body.singleton_metadata_init_va = Some(cursor);
cursor = match cursor.checked_add(12) {
Some(c) => c,
None => return,
};
}
MetadataInitializationKind::None | MetadataInitializationKind::Other => {}
}
if type_flags.class_has_vtable() {
match decode_vtable_header(rt, cursor) {
Some((header, end)) => {
body.vtable_header = Some(header);
cursor = end;
}
None => return,
}
}
if type_flags.class_has_override_table() {
match decode_override_header(rt, cursor) {
Some((header, end)) => {
body.override_table_header = Some(header);
cursor = end;
}
None => return,
}
}
let has_objc_stub = body
.extra_class_flags
.map(|f| (f & 0x1) != 0)
.unwrap_or(false);
if has_objc_stub {
body.objc_resilient_class_stub_va = Some(cursor);
cursor = match cursor.checked_add(4) {
Some(c) => c,
None => return,
};
}
if type_flags.has_canonical_metadata_prespecializations() {
match decode_prespecializations(rt, cursor) {
Some((count, base_va, end)) => {
body.prespecializations_count = Some(count);
body.prespecializations_base_va = Some(base_va);
cursor = end;
}
None => return,
}
}
if flags.has_invertible_protocols() {
match read_u16_at_va(rt, cursor) {
Some(bits) => {
body.invertible_protocol_set = Some(bits);
cursor = match cursor.checked_add(4) {
Some(c) => c,
None => return,
};
}
None => return,
}
}
if type_flags.has_singleton_metadata_pointer() {
body.singleton_metadata_pointer_va = Some(cursor);
cursor = match cursor.checked_add(4) {
Some(c) => c,
None => return,
};
}
if type_flags.class_has_default_override_table()
&& let Some((header, _end)) = decode_default_override_header(rt, cursor)
{
body.default_override_table_header = Some(header);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn decode_value_type_trailers(
rt: &SwiftRuntime<'_>,
flags: &ContextDescriptorFlags,
trailing_start: u64,
mut on_generic: impl FnMut(u64),
mut on_foreign: impl FnMut(u64),
mut on_singleton: impl FnMut(u64),
mut on_prespec: impl FnMut(u32, u64),
mut on_invertible: impl FnMut(u16),
mut on_singleton_pointer: impl FnMut(u64),
) {
let type_flags = flags.type_flags();
let mut cursor = trailing_start;
if flags.is_generic() {
if let Some((header_va, end)) = decode_generic_header(rt, cursor) {
on_generic(header_va);
cursor = end;
} else {
return;
}
}
match type_flags.metadata_initialization() {
MetadataInitializationKind::Foreign => {
on_foreign(cursor);
cursor = match cursor.checked_add(4) {
Some(c) => c,
None => return,
};
}
MetadataInitializationKind::Singleton => {
on_singleton(cursor);
cursor = match cursor.checked_add(12) {
Some(c) => c,
None => return,
};
}
MetadataInitializationKind::None | MetadataInitializationKind::Other => {}
}
if type_flags.has_canonical_metadata_prespecializations() {
match decode_prespecializations(rt, cursor) {
Some((count, base_va, end)) => {
on_prespec(count, base_va);
cursor = end;
}
None => return,
}
}
if flags.has_invertible_protocols() {
if let Some(bits) = read_u16_at_va(rt, cursor) {
on_invertible(bits);
cursor = match cursor.checked_add(4) {
Some(c) => c,
None => return,
};
} else {
return;
}
}
if type_flags.has_singleton_metadata_pointer() {
on_singleton_pointer(cursor);
}
}
fn decode_generic_header(rt: &SwiftRuntime<'_>, header_va: u64) -> Option<(u64, u64)> {
let header = rt.read_bytes(header_va, 16)?;
let num_params = read_u16_le_at(header, 8)?;
let num_requirements = read_u16_le_at(header, 10)?;
let mut cursor = header_va.checked_add(16)?;
let params_size = u64::from(num_params);
cursor = cursor.checked_add(params_size)?;
let pad = (4u64.wrapping_sub(params_size & 0x3)) & 0x3;
cursor = cursor.checked_add(pad)?;
let req_size = u64::from(num_requirements).checked_mul(12)?;
cursor = cursor.checked_add(req_size)?;
Some((header_va, cursor))
}
fn decode_vtable_header(rt: &SwiftRuntime<'_>, header_va: u64) -> Option<(VTableHeader, u64)> {
let bytes = rt.read_bytes(header_va, 8)?;
let vtable_offset = read_u32_le_at(bytes, 0)?;
let vtable_size = read_u32_le_at(bytes, 4)?;
let entries_va = header_va.checked_add(8)?;
let entries_size = u64::from(vtable_size).checked_mul(8)?;
let end = entries_va.checked_add(entries_size)?;
Some((
VTableHeader {
vtable_offset,
vtable_size,
entries_va,
},
end,
))
}
fn decode_override_header(
rt: &SwiftRuntime<'_>,
header_va: u64,
) -> Option<(OverrideTableHeader, u64)> {
let bytes = rt.read_bytes(header_va, 4)?;
let num_entries = read_u32_le_at(bytes, 0)?;
let entries_va = header_va.checked_add(4)?;
let entries_size = u64::from(num_entries).checked_mul(12)?;
let end = entries_va.checked_add(entries_size)?;
Some((
OverrideTableHeader {
num_entries,
entries_va,
},
end,
))
}
fn decode_default_override_header(
rt: &SwiftRuntime<'_>,
header_va: u64,
) -> Option<(DefaultOverrideTableHeader, u64)> {
let bytes = rt.read_bytes(header_va, 4)?;
let num_entries = read_u32_le_at(bytes, 0)?;
let entries_va = header_va.checked_add(4)?;
let entries_size = u64::from(num_entries).checked_mul(8)?;
let end = entries_va.checked_add(entries_size)?;
Some((
DefaultOverrideTableHeader {
num_entries,
entries_va,
},
end,
))
}
fn decode_prespecializations(rt: &SwiftRuntime<'_>, header_va: u64) -> Option<(u32, u64, u64)> {
let count_bytes = rt.read_bytes(header_va, 4)?;
let count = read_u32_le_at(count_bytes, 0)?;
let entries_va = header_va.checked_add(4)?;
let entries_size = u64::from(count).checked_mul(4)?;
let after_entries = entries_va.checked_add(entries_size)?;
let accessors_size = u64::from(count).checked_mul(4)?;
let end = after_entries.checked_add(accessors_size)?;
Some((count, entries_va, end))
}
fn read_u16_at_va(rt: &SwiftRuntime<'_>, va: u64) -> Option<u16> {
let bytes = rt.read_bytes(va, 2)?;
read_u16_le_at(bytes, 0)
}