use std::fmt;
use std::mem::offset_of;
use bitflags::bitflags;
use zerocopy::{FromBytes, Immutable, KnownLayout};
pub const DYLD_CACHE_MAGIC_PREFIX: &[u8; 4] = b"dyld";
pub const PAGE_SIZE_4K: u32 = 0x1000;
pub const PAGE_SIZE_16K: u32 = 0x4000;
pub const DYLD_CACHE_SLIDE_PAGE_ATTRS: u16 = 0xC000;
pub const DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA: u16 = 0x8000;
pub const DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE: u16 = 0x4000;
pub const DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE: u16 = 0xFFFF;
pub const DYLD_CACHE_SLIDE_V5_PAGE_ATTR_NO_REBASE: u16 = 0xFFFF;
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheHeader {
pub magic: [u8; 16],
pub mapping_offset: u32,
pub mapping_count: u32,
pub images_offset_old: u32,
pub images_count_old: u32,
pub dyld_base_address: u64,
pub code_signature_offset: u64,
pub code_signature_size: u64,
pub slide_info_offset_unused: u64,
pub slide_info_size_unused: u64,
pub local_symbols_offset: u64,
pub local_symbols_size: u64,
pub uuid: [u8; 16],
pub cache_type: u64,
pub branch_pools_offset: u32,
pub branch_pools_count: u32,
pub dyld_in_cache_mh: u64,
pub dyld_in_cache_entry: u64,
pub images_text_offset: u64,
pub images_text_count: u64,
pub patch_info_addr: u64,
pub patch_info_size: u64,
pub other_image_group_addr_unused: u64,
pub other_image_group_size_unused: u64,
pub prog_closures_addr: u64,
pub prog_closures_size: u64,
pub prog_closures_trie_addr: u64,
pub prog_closures_trie_size: u64,
pub platform: u32,
pub format_version_and_flags: u32,
pub shared_region_start: u64,
pub shared_region_size: u64,
pub max_slide: u64,
pub dylibs_image_array_addr: u64,
pub dylibs_image_array_size: u64,
pub dylibs_trie_addr: u64,
pub dylibs_trie_size: u64,
pub other_image_array_addr: u64,
pub other_image_array_size: u64,
pub other_trie_addr: u64,
pub other_trie_size: u64,
pub mapping_with_slide_offset: u32,
pub mapping_with_slide_count: u32,
pub dylibs_pbl_state_array_addr_unused: u64,
pub dylibs_pbl_set_addr: u64,
pub programs_pbl_set_pool_addr: u64,
pub programs_pbl_set_pool_size: u64,
pub program_trie_addr: u64,
pub program_trie_size: u32,
pub os_version: u32,
pub alt_platform: u32,
pub alt_os_version: u32,
pub swift_opts_offset: u64,
pub swift_opts_size: u64,
pub sub_cache_array_offset: u32,
pub sub_cache_array_count: u32,
pub symbol_file_uuid: [u8; 16],
pub rosetta_read_only_addr: u64,
pub rosetta_read_only_size: u64,
pub rosetta_read_write_addr: u64,
pub rosetta_read_write_size: u64,
pub images_offset: u32,
pub images_count: u32,
pub cache_sub_type: u32,
_pad1: u32,
pub objc_opts_offset: u64,
pub objc_opts_size: u64,
pub cache_atlas_offset: u64,
pub cache_atlas_size: u64,
pub dynamic_data_offset: u64,
pub dynamic_data_max_size: u64,
}
impl DyldCacheHeader {
pub fn architecture(&self) -> &str {
let magic_str = std::str::from_utf8(&self.magic).unwrap_or("");
magic_str
.trim_start_matches("dyld_v0")
.trim_start_matches("dyld_v1")
.trim()
}
pub fn contains_field(&self, field_offset: usize) -> bool {
field_offset < self.mapping_offset as usize
}
pub fn is_valid(&self) -> bool {
&self.magic[..4] == DYLD_CACHE_MAGIC_PREFIX
}
pub fn has_subcaches(&self) -> bool {
self.contains_field(offset_of!(Self, sub_cache_array_count))
&& self.sub_cache_array_count > 0
}
pub fn has_symbol_file(&self) -> bool {
self.contains_field(offset_of!(Self, symbol_file_uuid))
&& self.symbol_file_uuid != [0u8; 16]
}
pub fn uses_new_images_offset(&self) -> bool {
self.contains_field(offset_of!(Self, images_offset)) && self.images_offset != 0
}
pub fn actual_images_offset(&self) -> u64 {
if self.uses_new_images_offset() {
self.images_offset as u64
} else {
self.images_offset_old as u64
}
}
pub fn actual_images_count(&self) -> u64 {
if self.uses_new_images_offset() {
self.images_count as u64
} else {
self.images_count_old as u64
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromBytes, KnownLayout, Immutable)]
#[repr(transparent)]
pub struct DyldInfoFlags(pub u32);
impl DyldInfoFlags {
pub const HAS_SWIFT_PRECOMPUTED: u32 = 0x1;
pub const HAS_CODESIGNED_16K_PAGES: u32 = 0x2;
pub fn has(&self, flag: u32) -> bool {
(self.0 & flag) != 0
}
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheMappingInfo {
pub address: u64,
pub size: u64,
pub file_offset: u64,
pub max_prot: u32,
pub init_prot: u32,
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheMappingAndSlideInfo {
pub address: u64,
pub size: u64,
pub file_offset: u64,
pub slide_info_file_offset: u64,
pub slide_info_file_size: u64,
pub flags: u64,
pub max_prot: u32,
pub init_prot: u32,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MappingFlags: u64 {
const AUTH_DATA = 1 << 0;
const DIRTY_DATA = 1 << 1;
const CONST_DATA = 1 << 2;
const TEXT_STUBS = 1 << 3;
}
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheImageInfo {
pub address: u64,
pub mod_time: u64,
pub inode: u64,
pub path_file_offset: u32,
pub pad: u32,
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheImageTextInfo {
pub uuid: [u8; 16],
pub load_address: u64,
pub text_segment_size: u32,
pub path_offset: u32,
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheSlideInfo2 {
pub version: u32,
pub page_size: u32,
pub page_starts_offset: u32,
pub page_starts_count: u32,
pub page_extras_offset: u32,
pub page_extras_count: u32,
pub delta_mask: u64,
pub value_add: u64,
}
impl DyldCacheSlideInfo2 {
pub fn value_mask(&self) -> u64 {
!self.delta_mask
}
pub fn delta_shift(&self) -> u32 {
self.delta_mask.trailing_zeros()
}
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheSlideInfo3 {
pub version: u32,
pub page_size: u32,
pub page_starts_count: u32,
pub auth_value_add: u64,
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheSlideInfo5 {
pub version: u32,
pub page_size: u32,
pub page_starts_count: u32,
pub _pad: u32,
pub value_add: u64,
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct SlidePointer3(pub u64);
impl SlidePointer3 {
#[inline]
pub fn is_auth(&self) -> bool {
(self.0 >> 63) & 1 != 0
}
#[inline]
pub fn offset_to_next(&self) -> u64 {
(self.0 >> 51) & 0x7FF
}
#[inline]
pub fn auth_offset(&self) -> u32 {
(self.0 & 0xFFFFFFFF) as u32
}
#[inline]
pub fn plain_value(&self) -> u64 {
let value = self.0 & 0x0007_FFFF_FFFF_FFFF;
let top8 = ((self.0 >> 43) & 0xFF) as u8;
((top8 as u64) << 56) | value
}
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct SlidePointer5(pub u64);
impl SlidePointer5 {
#[inline]
pub fn is_auth(&self) -> bool {
(self.0 >> 63) & 1 != 0
}
#[inline]
pub fn next(&self) -> u64 {
(self.0 >> 51) & 0x7FF
}
#[inline]
pub fn runtime_offset(&self) -> u64 {
self.0 & 0x0007_FFFF_FFFF_FFFF
}
#[inline]
pub fn high8(&self) -> u8 {
((self.0 >> 43) & 0xFF) as u8
}
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldSubcacheEntry {
pub uuid: [u8; 16],
pub cache_vm_offset: u64,
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldSubcacheEntry2 {
pub uuid: [u8; 16],
pub cache_vm_offset: u64,
pub file_suffix: [u8; 32],
}
impl DyldSubcacheEntry2 {
pub fn suffix_str(&self) -> &str {
let end = self.file_suffix.iter().position(|&b| b == 0).unwrap_or(32);
std::str::from_utf8(&self.file_suffix[..end]).unwrap_or("")
}
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheLocalSymbolsInfo {
pub nlist_offset: u32,
pub nlist_count: u32,
pub strings_offset: u32,
pub strings_size: u32,
pub entries_offset: u32,
pub entries_count: u32,
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheLocalSymbolsEntry {
pub dylib_offset: u32,
pub nlist_start_index: u32,
pub nlist_count: u32,
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct DyldCacheLocalSymbolsEntry64 {
pub dylib_offset: u64,
pub nlist_start_index: u32,
pub nlist_count: u32,
}
impl fmt::Display for DyldCacheHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"DyldCache {{ magic: {:?}, arch: {}, uuid: {} }}",
std::str::from_utf8(&self.magic).unwrap_or("???"),
self.architecture(),
uuid_to_string(&self.uuid)
)
}
}
pub fn uuid_to_string(uuid: &[u8; 16]) -> String {
format!(
"{:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
uuid[0],
uuid[1],
uuid[2],
uuid[3],
uuid[4],
uuid[5],
uuid[6],
uuid[7],
uuid[8],
uuid[9],
uuid[10],
uuid[11],
uuid[12],
uuid[13],
uuid[14],
uuid[15]
)
}