apple_codesign/
macho_universal.rs1use {
6 goblin::mach::{
7 fat::{FatArch, FAT_MAGIC, SIZEOF_FAT_ARCH, SIZEOF_FAT_HEADER},
8 Mach,
9 },
10 scroll::{IOwrite, Pwrite},
11 std::io::Write,
12 thiserror::Error,
13};
14
15#[derive(Debug, Error)]
16pub enum UniversalMachOError {
17 #[error("I/O error: {0}")]
18 Io(#[from] std::io::Error),
19
20 #[error("mach-o parse error: {0}")]
21 Goblin(#[from] goblin::error::Error),
22
23 #[error("scroll error: {0}")]
24 Scroll(#[from] scroll::Error),
25}
26
27#[derive(Clone, Default)]
29pub struct UniversalBinaryBuilder {
30 binaries: Vec<Vec<u8>>,
31}
32
33impl UniversalBinaryBuilder {
34 pub fn add_binary(&mut self, data: impl AsRef<[u8]>) -> Result<usize, UniversalMachOError> {
35 let data = data.as_ref();
36
37 match Mach::parse(data)? {
38 Mach::Binary(_) => {
39 self.binaries.push(data.to_vec());
40 Ok(1)
41 }
42 Mach::Fat(multiarch) => {
43 for arch in multiarch.iter_arches() {
44 let arch = arch?;
45
46 let data =
47 &data[arch.offset as usize..arch.offset as usize + arch.size as usize];
48 self.binaries.push(data.to_vec());
49 }
50
51 Ok(multiarch.narches)
52 }
53 }
54 }
55
56 pub fn write(&self, writer: &mut impl Write) -> Result<(), UniversalMachOError> {
58 create_universal_macho(writer, self.binaries.iter().map(|x| x.as_slice()))
59 }
60}
61
62pub fn create_universal_macho<'a>(
69 writer: &mut impl Write,
70 binaries: impl Iterator<Item = &'a [u8]>,
71) -> Result<(), UniversalMachOError> {
72 const ALIGN_VALUE: u32 = 14;
76 let align: u32 = 2u32.pow(ALIGN_VALUE);
77
78 let mut records = vec![];
79
80 let mut offset: u32 = align;
81
82 for binary in binaries {
83 let macho = goblin::mach::MachO::parse(binary, 0)?;
84
85 let pad_bytes = match offset % align {
87 0 => 0,
88 x => align - x,
89 };
90
91 offset += pad_bytes;
92
93 let arch = FatArch {
94 cputype: macho.header.cputype,
95 cpusubtype: macho.header.cpusubtype,
96 offset,
97 size: binary.len() as u32,
98 align: ALIGN_VALUE,
99 };
100
101 offset += arch.size;
102
103 records.push((arch, pad_bytes as usize, binary));
104 }
105
106 writer.iowrite_with(FAT_MAGIC, scroll::BE)?;
108 writer.iowrite_with(records.len() as u32, scroll::BE)?;
109
110 for (fat_arch, _, _) in &records {
111 let mut buffer = [0u8; SIZEOF_FAT_ARCH];
112 buffer.pwrite_with(fat_arch, 0, scroll::BE)?;
113 writer.write_all(&buffer)?;
114 }
115
116 let current_offset = SIZEOF_FAT_HEADER + records.len() * SIZEOF_FAT_ARCH;
118 writer.write_all(&b"\0".repeat(align as usize - current_offset % align as usize))?;
119
120 assert!(current_offset <= align as usize, "too many mach-o entries");
122
123 for (_, pad_bytes, macho_data) in records {
124 writer.write_all(&b"\0".repeat(pad_bytes))?;
125 writer.write_all(macho_data)?;
126 }
127
128 Ok(())
129}