/rust/registry/src/index.crates.io-1949cf8c6b5b557f/goblin-0.10.7/src/mach/mod.rs
Line | Count | Source |
1 | | //! The Mach-o, mostly zero-copy, binary format parser and raw struct definitions |
2 | | use alloc::vec::Vec; |
3 | | use core::fmt; |
4 | | |
5 | | use log::debug; |
6 | | |
7 | | use scroll::ctx::SizeWith; |
8 | | use scroll::{BE, Pread}; |
9 | | |
10 | | use crate::{archive, container}; |
11 | | use crate::{error, take_hint_bytes}; |
12 | | |
13 | | pub mod bind_opcodes; |
14 | | pub mod constants; |
15 | | pub mod exports; |
16 | | pub mod fat; |
17 | | pub mod header; |
18 | | pub mod imports; |
19 | | pub mod load_command; |
20 | | pub mod relocation; |
21 | | pub mod segment; |
22 | | pub mod symbols; |
23 | | |
24 | | pub use self::constants::cputype; |
25 | | |
26 | | /// Returns a big endian magical number |
27 | 0 | pub fn peek(bytes: &[u8], offset: usize) -> error::Result<u32> { |
28 | 0 | Ok(bytes.pread_with::<u32>(offset, scroll::BE)?) |
29 | 0 | } |
30 | | |
31 | | /// Parses a magic number, and an accompanying mach-o binary parsing context, according to the magic number. |
32 | 16.4k | pub fn parse_magic_and_ctx( |
33 | 16.4k | bytes: &[u8], |
34 | 16.4k | offset: usize, |
35 | 16.4k | ) -> error::Result<(u32, Option<container::Ctx>)> { |
36 | | use crate::container::Container; |
37 | | use crate::mach::header::*; |
38 | 16.4k | let magic = bytes.pread_with::<u32>(offset, BE)?; |
39 | 8.34k | let ctx = match magic { |
40 | | MH_CIGAM_64 | MH_CIGAM | MH_MAGIC_64 | MH_MAGIC => { |
41 | 5.35k | let is_lsb = magic == MH_CIGAM || magic == MH_CIGAM_64; |
42 | 5.35k | let le = scroll::Endian::from(is_lsb); |
43 | 5.35k | let container = if magic == MH_MAGIC_64 || magic == MH_CIGAM_64 { |
44 | 3.48k | Container::Big |
45 | | } else { |
46 | 1.87k | Container::Little |
47 | | }; |
48 | 5.35k | Some(container::Ctx::new(container, le)) |
49 | | } |
50 | 2.98k | _ => None, |
51 | | }; |
52 | 8.34k | Ok((magic, ctx)) |
53 | 16.4k | } |
54 | | |
55 | | /// A cross-platform, zero-copy, endian-aware, 32/64 bit Mach-o binary parser |
56 | | pub struct MachO<'a> { |
57 | | /// The mach-o header |
58 | | pub header: header::Header, |
59 | | /// The load commands tell the kernel and dynamic linker how to use/interpret this binary |
60 | | pub load_commands: Vec<load_command::LoadCommand>, |
61 | | /// The load command "segments" - typically the pieces of the binary that are loaded into memory |
62 | | pub segments: segment::Segments<'a>, |
63 | | /// The "Nlist" style symbols in this binary - strippable |
64 | | pub symbols: Option<symbols::Symbols<'a>>, |
65 | | /// The dylibs this library depends on |
66 | | pub libs: Vec<&'a str>, |
67 | | /// The runtime search paths for dylibs this library depends on |
68 | | pub rpaths: Vec<&'a str>, |
69 | | /// The entry point (as a virtual memory address), 0 if none |
70 | | pub entry: u64, |
71 | | /// Whether `entry` refers to an older `LC_UNIXTHREAD` instead of the newer `LC_MAIN` entrypoint |
72 | | pub old_style_entry: bool, |
73 | | /// The name of the dylib, if any |
74 | | pub name: Option<&'a str>, |
75 | | /// Are we a little-endian binary? |
76 | | pub little_endian: bool, |
77 | | /// Are we a 64-bit binary |
78 | | pub is_64: bool, |
79 | | data: &'a [u8], |
80 | | ctx: container::Ctx, |
81 | | export_trie: Option<exports::ExportTrie<'a>>, |
82 | | bind_interpreter: Option<imports::BindInterpreter<'a>>, |
83 | | } |
84 | | |
85 | | impl<'a> fmt::Debug for MachO<'a> { |
86 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
87 | 0 | fmt.debug_struct("MachO") |
88 | 0 | .field("header", &self.header) |
89 | 0 | .field("load_commands", &self.load_commands) |
90 | 0 | .field("segments", &self.segments) |
91 | 0 | .field("entry", &self.entry) |
92 | 0 | .field("old_style_entry", &self.old_style_entry) |
93 | 0 | .field("libs", &self.libs) |
94 | 0 | .field("name", &self.name) |
95 | 0 | .field("little_endian", &self.little_endian) |
96 | 0 | .field("is_64", &self.is_64) |
97 | 0 | .field("symbols()", &self.symbols().collect::<Vec<_>>()) |
98 | 0 | .field("exports()", &self.exports()) |
99 | 0 | .field("imports()", &self.imports()) |
100 | 0 | .finish() |
101 | 0 | } |
102 | | } |
103 | | |
104 | | impl<'a> MachO<'a> { |
105 | | /// Is this a relocatable object file? |
106 | 0 | pub fn is_object_file(&self) -> bool { |
107 | 0 | self.header.filetype == header::MH_OBJECT |
108 | 0 | } |
109 | | /// Return an iterator over all the symbols in this binary |
110 | 3.91k | pub fn symbols(&self) -> symbols::SymbolIterator<'a> { |
111 | 3.91k | if let Some(ref symbols) = self.symbols { |
112 | 2.60k | symbols.into_iter() |
113 | | } else { |
114 | 1.30k | symbols::SymbolIterator::default() |
115 | | } |
116 | 3.91k | } |
117 | | /// Return a vector of the relocations in this binary |
118 | 0 | pub fn relocations( |
119 | 0 | &self, |
120 | 0 | ) -> error::Result<Vec<(usize, segment::RelocationIterator<'_>, segment::Section)>> { |
121 | 0 | debug!("Iterating relocations"); |
122 | 0 | let mut relocs = Vec::new(); |
123 | 0 | for (_i, segment) in (&self.segments).into_iter().enumerate() { |
124 | 0 | for (j, section) in segment.into_iter().enumerate() { |
125 | 0 | let (section, _data) = section?; |
126 | 0 | if section.nreloc > 0 { |
127 | 0 | relocs.push((j, section.iter_relocations(self.data, self.ctx), section)); |
128 | 0 | } |
129 | | } |
130 | | } |
131 | 0 | Ok(relocs) |
132 | 0 | } |
133 | | /// Return the exported symbols in this binary (if any) |
134 | 0 | pub fn exports(&self) -> error::Result<Vec<exports::Export<'_>>> { |
135 | 0 | if let Some(ref trie) = self.export_trie { |
136 | 0 | trie.exports(self.libs.as_slice()) |
137 | | } else { |
138 | 0 | Ok(vec![]) |
139 | | } |
140 | 0 | } |
141 | | /// Return the imported symbols in this binary that dyld knows about (if any) |
142 | 0 | pub fn imports(&self) -> error::Result<Vec<imports::Import<'_>>> { |
143 | 0 | if let Some(ref interpreter) = self.bind_interpreter { |
144 | 0 | interpreter.imports(self.libs.as_slice(), self.segments.as_slice(), self.ctx) |
145 | | } else { |
146 | 0 | Ok(vec![]) |
147 | | } |
148 | 0 | } |
149 | | /// Parses the Mach-o binary from `bytes` at `offset` |
150 | 12.9k | pub fn parse(bytes: &'a [u8], offset: usize) -> error::Result<MachO<'a>> { |
151 | 12.9k | Self::parse_impl(bytes, offset, false) |
152 | 12.9k | } |
153 | | |
154 | | /// Parses the Mach-o binary from `bytes` at `offset` in lossy mode |
155 | 0 | pub fn parse_lossy(bytes: &'a [u8], offset: usize) -> error::Result<MachO<'a>> { |
156 | 0 | Self::parse_impl(bytes, offset, true) |
157 | 0 | } |
158 | | |
159 | | /// Parses the Mach-o binary from `bytes` at `offset` in `lossy` mode |
160 | 12.9k | fn parse_impl(bytes: &'a [u8], mut offset: usize, lossy: bool) -> error::Result<MachO<'a>> { |
161 | 12.9k | let (magic, maybe_ctx) = parse_magic_and_ctx(bytes, offset)?; |
162 | 4.83k | let ctx = if let Some(ctx) = maybe_ctx { |
163 | 2.84k | ctx |
164 | | } else { |
165 | 1.98k | return Err(error::Error::BadMagic(u64::from(magic))); |
166 | | }; |
167 | 2.84k | debug!("Ctx: {:?}", ctx); |
168 | 2.84k | let offset = &mut offset; |
169 | 2.84k | let header: header::Header = bytes.pread_with(*offset, ctx)?; |
170 | 2.81k | debug!("Mach-o header: {:?}", header); |
171 | 2.81k | let little_endian = ctx.le.is_little(); |
172 | 2.81k | let is_64 = ctx.container.is_big(); |
173 | 2.81k | *offset += header::Header::size_with(&ctx.container); |
174 | 2.81k | let ncmds = header.ncmds; |
175 | | |
176 | 2.81k | let sizeofcmds = header.sizeofcmds as usize; |
177 | | // a load cmd is at least 2 * 4 bytes, (type, sizeof) |
178 | 2.81k | if ncmds > sizeofcmds / 8 || sizeofcmds > bytes.len() { |
179 | 113 | return Err(error::Error::BufferTooShort(ncmds, "load commands")); |
180 | 2.70k | } |
181 | | |
182 | 2.70k | let mut cmds: Vec<load_command::LoadCommand> = Vec::with_capacity(ncmds); |
183 | 2.70k | let mut symbols = None; |
184 | 2.70k | let mut libs = vec!["self"]; |
185 | 2.70k | let mut rpaths = vec![]; |
186 | 2.70k | let mut export_trie = None; |
187 | 2.70k | let mut bind_interpreter = None; |
188 | 2.70k | let mut unixthread_entry_address = None; |
189 | 2.70k | let mut main_entry_offset = None; |
190 | 2.70k | let mut name = None; |
191 | 2.70k | let mut segments = segment::Segments::new(ctx); |
192 | 24.1k | for i in 0..ncmds { |
193 | 24.1k | let cmd = load_command::LoadCommand::parse(bytes, offset, ctx.le)?; |
194 | 23.7k | debug!("{} - {:?}", i, cmd); |
195 | 23.7k | match cmd.command { |
196 | 424 | load_command::CommandVariant::Segment32(command) => segments.push( |
197 | 424 | segment::Segment::from_32_impl(bytes, &command, cmd.offset, ctx, lossy)?, |
198 | | ), |
199 | 4.84k | load_command::CommandVariant::Segment64(command) => segments.push( |
200 | 4.84k | segment::Segment::from_64_impl(bytes, &command, cmd.offset, ctx, lossy)?, |
201 | | ), |
202 | 1.78k | load_command::CommandVariant::Symtab(command) => { |
203 | 1.78k | match symbols::Symbols::parse(bytes, &command, ctx) { |
204 | 1.73k | Ok(s) => symbols = Some(s), |
205 | 0 | Err(e) if lossy => { |
206 | 0 | debug!("CommandVariant::Symtab failed: {e}"); |
207 | | } |
208 | 49 | Err(e) => return Err(e), |
209 | | } |
210 | | } |
211 | 65 | load_command::CommandVariant::LoadDylib(command) |
212 | 0 | | load_command::CommandVariant::LoadUpwardDylib(command) |
213 | 0 | | load_command::CommandVariant::ReexportDylib(command) |
214 | 0 | | load_command::CommandVariant::LoadWeakDylib(command) |
215 | 54 | | load_command::CommandVariant::LazyLoadDylib(command) => { |
216 | 119 | match bytes.pread::<&str>(cmd.offset + command.dylib.name as usize) { |
217 | 101 | Ok(lib) => libs.push(lib), |
218 | 0 | Err(e) if lossy => { |
219 | 0 | debug!("CommandVariant::Load/Reexport Dylib failed: {e}"); |
220 | | } |
221 | 18 | Err(e) => return Err(e.into()), |
222 | | } |
223 | | } |
224 | 0 | load_command::CommandVariant::Rpath(command) => { |
225 | 0 | match bytes.pread::<&str>(cmd.offset + command.path as usize) { |
226 | 0 | Ok(rpath) => rpaths.push(rpath), |
227 | 0 | Err(e) if lossy => { |
228 | 0 | debug!("CommandVariant::Rpath failed: {e}"); |
229 | | } |
230 | 0 | Err(e) => return Err(e.into()), |
231 | | } |
232 | | } |
233 | 77 | load_command::CommandVariant::DyldInfo(command) |
234 | 94 | | load_command::CommandVariant::DyldInfoOnly(command) => { |
235 | 94 | export_trie = Some(exports::ExportTrie::new(bytes, &command)); |
236 | 94 | bind_interpreter = Some(imports::BindInterpreter::new(bytes, &command)); |
237 | 94 | } |
238 | 3 | load_command::CommandVariant::DyldExportsTrie(command) => { |
239 | 3 | export_trie = Some(exports::ExportTrie::new_from_linkedit_data_command( |
240 | 3 | bytes, &command, |
241 | 3 | )); |
242 | 3 | } |
243 | 24 | load_command::CommandVariant::Unixthread(command) => { |
244 | | // dyld cares only about the first LC_UNIXTHREAD |
245 | 24 | if unixthread_entry_address.is_none() { |
246 | | unixthread_entry_address = |
247 | 24 | Some(command.instruction_pointer(header.cputype)?); |
248 | 0 | } |
249 | | } |
250 | 17 | load_command::CommandVariant::Main(command) => { |
251 | | // dyld cares only about the first LC_MAIN |
252 | 17 | if main_entry_offset.is_none() { |
253 | 17 | main_entry_offset = Some(command.entryoff); |
254 | 17 | } |
255 | | } |
256 | 32 | load_command::CommandVariant::IdDylib(command) => { |
257 | 32 | match bytes.pread::<&str>(cmd.offset + command.dylib.name as usize) { |
258 | 26 | Ok(id) => { |
259 | 26 | libs[0] = id; |
260 | 26 | name = Some(id); |
261 | 26 | } |
262 | 0 | Err(e) if lossy => { |
263 | 0 | debug!("CommandVariant::IdDylib failed: {e}"); |
264 | | } |
265 | 6 | Err(e) => return Err(e.into()), |
266 | | } |
267 | | } |
268 | 16.4k | _ => (), |
269 | | } |
270 | 23.4k | cmds.push(cmd) |
271 | | } |
272 | | |
273 | | // dyld prefers LC_MAIN over LC_UNIXTHREAD |
274 | | // choose the same way here |
275 | 1.95k | let (entry, old_style_entry) = if let Some(offset) = main_entry_offset { |
276 | | // map the entrypoint offset to a virtual memory address |
277 | 17 | let base_address = segments |
278 | 17 | .iter() |
279 | 34 | .filter(|s| &s.segname[0..7] == b"__TEXT\0") |
280 | 17 | .map(|s| s.vmaddr - s.fileoff) |
281 | 17 | .next() |
282 | 17 | .ok_or_else(|| { |
283 | 0 | error::Error::Malformed(format!( |
284 | 0 | "image specifies LC_MAIN offset {} but has no __TEXT segment", |
285 | 0 | offset |
286 | 0 | )) |
287 | 0 | })?; |
288 | | |
289 | 17 | (base_address + offset, false) |
290 | 1.93k | } else if let Some(address) = unixthread_entry_address { |
291 | 0 | (address, true) |
292 | | } else { |
293 | 1.93k | (0, false) |
294 | | }; |
295 | | |
296 | 1.95k | Ok(MachO { |
297 | 1.95k | header, |
298 | 1.95k | load_commands: cmds, |
299 | 1.95k | segments, |
300 | 1.95k | symbols, |
301 | 1.95k | libs, |
302 | 1.95k | rpaths, |
303 | 1.95k | export_trie, |
304 | 1.95k | bind_interpreter, |
305 | 1.95k | entry, |
306 | 1.95k | old_style_entry, |
307 | 1.95k | name, |
308 | 1.95k | ctx, |
309 | 1.95k | is_64, |
310 | 1.95k | little_endian, |
311 | 1.95k | data: bytes, |
312 | 1.95k | }) |
313 | 12.9k | } |
314 | | } |
315 | | |
316 | | /// A Mach-o multi architecture (Fat) binary container |
317 | | pub struct MultiArch<'a> { |
318 | | data: &'a [u8], |
319 | | start: usize, |
320 | | pub narches: usize, |
321 | | } |
322 | | |
323 | | /// Iterator over the fat architecture headers in a `MultiArch` container |
324 | | pub struct FatArchIterator<'a> { |
325 | | index: usize, |
326 | | data: &'a [u8], |
327 | | narches: usize, |
328 | | start: usize, |
329 | | } |
330 | | |
331 | | /// A single architecture froma multi architecture binary container |
332 | | /// ([MultiArch]). |
333 | | #[derive(Debug)] |
334 | | #[allow(clippy::large_enum_variant)] |
335 | | pub enum SingleArch<'a> { |
336 | | MachO(MachO<'a>), |
337 | | Archive(archive::Archive<'a>), |
338 | | } |
339 | | |
340 | | impl<'a> Iterator for FatArchIterator<'a> { |
341 | | type Item = error::Result<fat::FatArch>; |
342 | 121k | fn next(&mut self) -> Option<Self::Item> { |
343 | 121k | if self.index >= self.narches { |
344 | 0 | None |
345 | | } else { |
346 | 121k | let offset = (self.index * fat::SIZEOF_FAT_ARCH) + self.start; |
347 | 121k | let arch = self |
348 | 121k | .data |
349 | 121k | .pread_with::<fat::FatArch>(offset, scroll::BE) |
350 | 121k | .map_err(core::convert::Into::into); |
351 | 121k | self.index += 1; |
352 | 121k | Some(arch) |
353 | | } |
354 | 121k | } |
355 | | } |
356 | | |
357 | | /// Iterator over every entry contained in this `MultiArch` container |
358 | | pub struct SingleArchIterator<'a> { |
359 | | index: usize, |
360 | | data: &'a [u8], |
361 | | narches: usize, |
362 | | start: usize, |
363 | | } |
364 | | |
365 | 0 | pub fn peek_bytes(bytes: &[u8; 16]) -> error::Result<crate::Hint> { |
366 | 0 | if &bytes[0..archive::SIZEOF_MAGIC] == archive::MAGIC { |
367 | 0 | Ok(crate::Hint::Archive) |
368 | | } else { |
369 | 0 | let (magic, maybe_ctx) = parse_magic_and_ctx(bytes, 0)?; |
370 | 0 | match magic { |
371 | | header::MH_CIGAM_64 | header::MH_CIGAM | header::MH_MAGIC_64 | header::MH_MAGIC => { |
372 | 0 | if let Some(ctx) = maybe_ctx { |
373 | 0 | Ok(crate::Hint::Mach(crate::HintData { |
374 | 0 | is_lsb: ctx.le.is_little(), |
375 | 0 | is_64: Some(ctx.container.is_big()), |
376 | 0 | })) |
377 | | } else { |
378 | 0 | Err(error::Error::Malformed(format!( |
379 | 0 | "Correct mach magic {:#x} does not have a matching parsing context!", |
380 | 0 | magic |
381 | 0 | ))) |
382 | | } |
383 | | } |
384 | | fat::FAT_MAGIC => { |
385 | | // should probably verify this is always Big Endian... |
386 | 0 | let narchitectures = bytes.pread_with::<u32>(4, BE)? as usize; |
387 | 0 | Ok(crate::Hint::MachFat(narchitectures)) |
388 | | } |
389 | 0 | _ => Ok(crate::Hint::Unknown(bytes.pread::<u64>(0)?)), |
390 | | } |
391 | | } |
392 | 0 | } |
393 | | |
394 | 0 | fn extract_multi_entry(bytes: &[u8]) -> error::Result<SingleArch<'_>> { |
395 | 0 | if let Some(hint_bytes) = take_hint_bytes(bytes) { |
396 | 0 | match peek_bytes(hint_bytes)? { |
397 | | crate::Hint::Mach(_) => { |
398 | 0 | let binary = MachO::parse(bytes, 0)?; |
399 | 0 | Ok(SingleArch::MachO(binary)) |
400 | | } |
401 | | crate::Hint::Archive => { |
402 | 0 | let archive = archive::Archive::parse(bytes)?; |
403 | 0 | Ok(SingleArch::Archive(archive)) |
404 | | } |
405 | 0 | _ => Err(error::Error::Malformed(format!( |
406 | 0 | "multi-arch entry must be a Mach-O binary or an archive" |
407 | 0 | ))), |
408 | | } |
409 | | } else { |
410 | 0 | Err(error::Error::Malformed(format!("Object is too small"))) |
411 | | } |
412 | 0 | } |
413 | | |
414 | | impl<'a> Iterator for SingleArchIterator<'a> { |
415 | | type Item = error::Result<SingleArch<'a>>; |
416 | 0 | fn next(&mut self) -> Option<Self::Item> { |
417 | 0 | if self.index >= self.narches { |
418 | 0 | None |
419 | | } else { |
420 | 0 | let index = self.index; |
421 | 0 | let offset = (index * fat::SIZEOF_FAT_ARCH) + self.start; |
422 | 0 | self.index += 1; |
423 | 0 | match self.data.pread_with::<fat::FatArch>(offset, scroll::BE) { |
424 | 0 | Ok(arch) => { |
425 | 0 | let bytes = arch.slice(self.data); |
426 | 0 | Some(extract_multi_entry(bytes)) |
427 | | } |
428 | 0 | Err(e) => Some(Err(e.into())), |
429 | | } |
430 | | } |
431 | 0 | } |
432 | | } |
433 | | |
434 | | impl<'a, 'b> IntoIterator for &'b MultiArch<'a> { |
435 | | type Item = error::Result<SingleArch<'a>>; |
436 | | type IntoIter = SingleArchIterator<'a>; |
437 | 0 | fn into_iter(self) -> Self::IntoIter { |
438 | 0 | SingleArchIterator { |
439 | 0 | index: 0, |
440 | 0 | data: self.data, |
441 | 0 | narches: self.narches, |
442 | 0 | start: self.start, |
443 | 0 | } |
444 | 0 | } |
445 | | } |
446 | | |
447 | | impl<'a> MultiArch<'a> { |
448 | | /// Lazily construct `Self` |
449 | 242 | pub fn new(bytes: &'a [u8]) -> error::Result<Self> { |
450 | 242 | let header = fat::FatHeader::parse(bytes)?; |
451 | 242 | Ok(MultiArch { |
452 | 242 | data: bytes, |
453 | 242 | start: fat::SIZEOF_FAT_HEADER, |
454 | 242 | narches: header.nfat_arch as usize, |
455 | 242 | }) |
456 | 242 | } |
457 | | /// Iterate every fat arch header |
458 | 6.48k | pub fn iter_arches(&self) -> FatArchIterator<'_> { |
459 | 6.48k | FatArchIterator { |
460 | 6.48k | index: 0, |
461 | 6.48k | data: self.data, |
462 | 6.48k | narches: self.narches, |
463 | 6.48k | start: self.start, |
464 | 6.48k | } |
465 | 6.48k | } |
466 | | /// Return all the architectures in this binary |
467 | 0 | pub fn arches(&self) -> error::Result<Vec<fat::FatArch>> { |
468 | 0 | if self.narches > self.data.len() / fat::SIZEOF_FAT_ARCH { |
469 | 0 | return Err(error::Error::BufferTooShort(self.narches, "arches")); |
470 | 0 | } |
471 | | |
472 | 0 | let mut arches = Vec::with_capacity(self.narches); |
473 | 0 | for arch in self.iter_arches() { |
474 | 0 | arches.push(arch?); |
475 | | } |
476 | 0 | Ok(arches) |
477 | 0 | } |
478 | | /// Try to get the Mach-o binary at `index` |
479 | 0 | pub fn get(&self, index: usize) -> error::Result<SingleArch<'a>> { |
480 | 0 | if index >= self.narches { |
481 | 0 | return Err(error::Error::Malformed(format!( |
482 | 0 | "Requested the {}-th binary, but there are only {} architectures in this container", |
483 | 0 | index, self.narches |
484 | 0 | ))); |
485 | 0 | } |
486 | 0 | let offset = (index * fat::SIZEOF_FAT_ARCH) + self.start; |
487 | 0 | let arch = self.data.pread_with::<fat::FatArch>(offset, scroll::BE)?; |
488 | 0 | let bytes = arch.slice(self.data); |
489 | 0 | extract_multi_entry(bytes) |
490 | 0 | } |
491 | | |
492 | 0 | pub fn find<F: Fn(error::Result<fat::FatArch>) -> bool>( |
493 | 0 | &'a self, |
494 | 0 | f: F, |
495 | 0 | ) -> Option<error::Result<SingleArch<'a>>> { |
496 | 0 | for (i, arch) in self.iter_arches().enumerate() { |
497 | 0 | if f(arch) { |
498 | 0 | return Some(self.get(i)); |
499 | 0 | } |
500 | | } |
501 | 0 | None |
502 | 0 | } |
503 | | /// Try and find the `cputype` in `Self`, if there is one |
504 | 0 | pub fn find_cputype(&self, cputype: u32) -> error::Result<Option<fat::FatArch>> { |
505 | 0 | for arch in self.iter_arches() { |
506 | 0 | let arch = arch?; |
507 | 0 | if arch.cputype == cputype { |
508 | 0 | return Ok(Some(arch)); |
509 | 0 | } |
510 | | } |
511 | 0 | Ok(None) |
512 | 0 | } |
513 | | } |
514 | | |
515 | | impl<'a> fmt::Debug for MultiArch<'a> { |
516 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
517 | 0 | fmt.debug_struct("MultiArch") |
518 | 0 | .field("arches", &self.arches().unwrap_or_default()) |
519 | 0 | .field("data", &self.data.len()) |
520 | 0 | .finish() |
521 | 0 | } |
522 | | } |
523 | | |
524 | | #[derive(Debug)] |
525 | | #[allow(clippy::large_enum_variant)] |
526 | | /// Either a collection of multiple architectures, or a single mach-o binary |
527 | | pub enum Mach<'a> { |
528 | | /// A "fat" multi-architecture binary container |
529 | | Fat(MultiArch<'a>), |
530 | | /// A regular Mach-o binary |
531 | | Binary(MachO<'a>), |
532 | | } |
533 | | |
534 | | impl<'a> Mach<'a> { |
535 | | /// Parse from `bytes` either a multi-arch binary or a regular mach-o binary |
536 | 0 | pub fn parse(bytes: &'a [u8]) -> error::Result<Self> { |
537 | 0 | Self::parse_impl(bytes, false) |
538 | 0 | } |
539 | | |
540 | | /// Parse from `bytes` either a multi-arch binary or a regular mach-o binary in lossy mode |
541 | 0 | pub fn parse_lossy(bytes: &'a [u8]) -> error::Result<Self> { |
542 | 0 | Self::parse_impl(bytes, true) |
543 | 0 | } |
544 | | |
545 | | /// Parse from `bytes` either a multi-arch binary or a regular mach-o binary |
546 | 0 | fn parse_impl(bytes: &'a [u8], lossy: bool) -> error::Result<Self> { |
547 | 0 | let size = bytes.len(); |
548 | 0 | if size < 4 { |
549 | 0 | let error = error::Error::Malformed("size is smaller than a magical number".into()); |
550 | 0 | return Err(error); |
551 | 0 | } |
552 | 0 | let magic = peek(&bytes, 0)?; |
553 | 0 | match magic { |
554 | | fat::FAT_MAGIC => { |
555 | 0 | let multi = MultiArch::new(bytes)?; |
556 | 0 | Ok(Mach::Fat(multi)) |
557 | | } |
558 | | // we might be a regular binary |
559 | | _ => { |
560 | 0 | let binary = MachO::parse_impl(bytes, 0, lossy)?; |
561 | 0 | Ok(Mach::Binary(binary)) |
562 | | } |
563 | | } |
564 | 0 | } |
565 | | } |
566 | | |
567 | | #[cfg(test)] |
568 | | mod test { |
569 | | use super::{Mach, SingleArch}; |
570 | | |
571 | | #[test] |
572 | | fn parse_multi_arch_of_macho_binaries() { |
573 | | // Create via: |
574 | | // clang -arch arm64 -shared -o /tmp/hello_world_arm hello_world.c |
575 | | // clang -arch x86_64 -shared -o /tmp/hello_world_x86_64 hello_world.c |
576 | | // lipo -create -output hello_world_fat_binaries /tmp/hello_world_arm /tmp/hello_world_x86_64 |
577 | | // strip hello_world_fat_binaries |
578 | | let bytes = include_bytes!(concat!( |
579 | | env!("CARGO_MANIFEST_DIR"), |
580 | | "/assets/hello_world_fat_binaries" |
581 | | )); |
582 | | let mach = Mach::parse(bytes).expect("failed to parse input file"); |
583 | | match mach { |
584 | | Mach::Fat(fat) => { |
585 | | assert!(fat.into_iter().count() > 0); |
586 | | for entry in fat.into_iter() { |
587 | | let entry = entry.expect("failed to read entry"); |
588 | | match entry { |
589 | | SingleArch::MachO(macho) => { |
590 | | assert!(macho.symbols().count() > 0); |
591 | | } |
592 | | _ => panic!("expected MultiArchEntry::MachO, got {:?}", entry), |
593 | | } |
594 | | } |
595 | | } |
596 | | Mach::Binary(_) => panic!("expected Mach::Fat, got Mach::Binary"), |
597 | | } |
598 | | } |
599 | | |
600 | | #[test] |
601 | | fn parse_multi_arch_of_archives() { |
602 | | // Created with: |
603 | | // clang -c -o /tmp/hello_world.o hello_world.c |
604 | | // ar -r /tmp/hello_world.a /tmp/hello_world.o |
605 | | // lipo -create -output hello_world_fat_archives /tmp/hello_world.a |
606 | | // strip hello_world_fat_archives |
607 | | let bytes = include_bytes!(concat!( |
608 | | env!("CARGO_MANIFEST_DIR"), |
609 | | "/assets/hello_world_fat_archives" |
610 | | )); |
611 | | let mach = Mach::parse(bytes).expect("failed to parse input file"); |
612 | | match mach { |
613 | | Mach::Fat(fat) => { |
614 | | assert!(fat.into_iter().count() > 0); |
615 | | for entry in fat.into_iter() { |
616 | | let entry = entry.expect("failed to read entry"); |
617 | | match entry { |
618 | | SingleArch::Archive(archive) => { |
619 | | assert!(!archive.members().is_empty()) |
620 | | } |
621 | | _ => panic!("expected MultiArchEntry::Archive, got {:?}", entry), |
622 | | } |
623 | | } |
624 | | } |
625 | | Mach::Binary(_) => panic!("expected Mach::Fat, got Mach::Binary"), |
626 | | } |
627 | | } |
628 | | } |