Git · dadoum.zone
Repositories
Help
Report an Issue
apple-crates
Code
Commits
Branches
Tags
Search
Tree:
a74924e
Branches
Tags
main
apple-crates
apple-codesign-quick
src
macho
fat.rs
Refactor and unify a lot of things for another project and implement airdrop support
Dadoum
commited
a74924e
at 2026-10-25 22:15:58
fat.rs
Blame
History
Raw
use super::{align_to, checked_add, ensure_len}; use crate::error::{CodeSignError, Result}; use byteorder::{BigEndian, ByteOrder}; use std::ops::Range; use std::path::Path; pub(super) const FAT_MAGIC: u32 = 0xcafe_babe; const FAT_MAGIC_64: u32 = 0xcafe_babf; const FAT_HEADER_LEN: usize = 8; const FAT_ARCH_LEN: usize = 20; const CPU_TYPE_ARM64: i32 = 0x0100_000c; // lipo's MAXSECTALIGN; real slices are aligned on 2^14. const MAX_ARCH_ALIGN: u32 = 15; #[derive(Clone, Copy, Debug)] pub(super) struct FatArch { cpu_type: i32, cpu_subtype: i32, offset: usize, size: usize, align: u32, } impl FatArch { pub(super) fn range(&self) -> Range<usize> { self.offset..self.offset + self.size } pub(super) fn is_arm64(&self) -> bool { self.cpu_type == CPU_TYPE_ARM64 } } /// The architectures of a fat Mach-O, or None if it is a thin one. pub(super) fn parse_fat_arches(path: &Path, data: &[u8]) -> Result<Option<Vec<FatArch>>> { match data.get(..4).map(BigEndian::read_u32) { Some(FAT_MAGIC) => parse_fat_table(path, data).map(Some), Some(FAT_MAGIC_64) => Err(CodeSignError::macho( path, "64-bit fat Mach-O headers are not supported yet", )), Some(_) => Ok(None), None => Err(CodeSignError::macho(path, "the file is too small")), } } fn parse_fat_table(path: &Path, data: &[u8]) -> Result<Vec<FatArch>> { ensure_len(path, data, FAT_HEADER_LEN, "fat header")?; let arch_count = BigEndian::read_u32(&data[4..]) as usize; let table_end = arch_count .checked_mul(FAT_ARCH_LEN) .and_then(|table_len| table_len.checked_add(FAT_HEADER_LEN)) .ok_or_else(|| CodeSignError::macho(path, "the fat architecture table overflows"))?; ensure_len(path, data, table_end, "fat architecture table")?; if arch_count == 0 { return Err(CodeSignError::macho( path, "the fat Mach-O has no architecture", )); } data[FAT_HEADER_LEN..table_end] .chunks(FAT_ARCH_LEN) .map(|record| { let arch = FatArch { cpu_type: BigEndian::read_i32(record), cpu_subtype: BigEndian::read_i32(&record[4..]), offset: BigEndian::read_u32(&record[8..]) as usize, size: BigEndian::read_u32(&record[12..]) as usize, align: BigEndian::read_u32(&record[16..]), }; let end = checked_add(path, arch.offset, arch.size, "fat architecture slice")?; ensure_len(path, data, end, "fat architecture slice")?; if arch.align <= MAX_ARCH_ALIGN { Ok(arch) } else { Err(CodeSignError::macho( path, format!("invalid fat architecture alignment 2^{}", arch.align), )) } }) .collect() } /// Lay the signed slices out again, each one at its architecture's alignment. pub(super) fn build_fat(path: &Path, arches: &[FatArch], slices: &[Vec<u8>]) -> Result<Vec<u8>> { let table_end = FAT_HEADER_LEN + arches.len() * FAT_ARCH_LEN; let mut table = Vec::with_capacity(table_end); table.extend_from_slice(&FAT_MAGIC.to_be_bytes()); table.extend_from_slice(&(arches.len() as u32).to_be_bytes()); let mut offsets = Vec::with_capacity(arches.len()); let mut end = table_end; for (arch, slice) in arches.iter().zip(slices) { let offset = align_to(end, 1 << arch.align); end = offset + slice.len(); let too_big = || CodeSignError::macho(path, "the fat Mach-O outgrew 32-bit offsets"); let offset_u32 = u32::try_from(offset).map_err(|_| too_big())?; let size_u32 = u32::try_from(slice.len()).map_err(|_| too_big())?; u32::try_from(end).map_err(|_| too_big())?; for value in [ arch.cpu_type as u32, arch.cpu_subtype as u32, offset_u32, size_u32, arch.align, ] { table.extend_from_slice(&value.to_be_bytes()); } offsets.push(offset); } let mut out = Vec::with_capacity(end); out.extend_from_slice(&table); for (offset, slice) in offsets.into_iter().zip(slices) { out.resize(offset, 0); out.extend_from_slice(slice); } Ok(out) }