@@ -7,8 +7,8 @@ use log::debug;
7
7
use scroll:: ctx:: SizeWith ;
8
8
use scroll:: { Pread , BE } ;
9
9
10
- use crate :: container;
11
- use crate :: error;
10
+ use crate :: { archive , container} ;
11
+ use crate :: { error, take_hint_bytes } ;
12
12
13
13
pub mod bind_opcodes;
14
14
pub mod constants;
@@ -296,6 +296,15 @@ pub struct FatArchIterator<'a> {
296
296
start : usize ,
297
297
}
298
298
299
+ /// A single architecture froma multi architecture binary container
300
+ /// ([MultiArch]).
301
+ #[ derive( Debug ) ]
302
+ #[ allow( clippy:: large_enum_variant) ]
303
+ pub enum SingleArch < ' a > {
304
+ MachO ( MachO < ' a > ) ,
305
+ Archive ( archive:: Archive < ' a > ) ,
306
+ }
307
+
299
308
impl < ' a > Iterator for FatArchIterator < ' a > {
300
309
type Item = error:: Result < fat:: FatArch > ;
301
310
fn next ( & mut self ) -> Option < Self :: Item > {
@@ -313,16 +322,65 @@ impl<'a> Iterator for FatArchIterator<'a> {
313
322
}
314
323
}
315
324
316
- /// Iterator over every `MachO` binary contained in this `MultiArch` container
317
- pub struct MachOIterator < ' a > {
325
+ /// Iterator over every entry contained in this `MultiArch` container
326
+ pub struct SingleArchIterator < ' a > {
318
327
index : usize ,
319
328
data : & ' a [ u8 ] ,
320
329
narches : usize ,
321
330
start : usize ,
322
331
}
323
332
324
- impl < ' a > Iterator for MachOIterator < ' a > {
325
- type Item = error:: Result < MachO < ' a > > ;
333
+ pub fn peek_bytes ( bytes : & [ u8 ; 16 ] ) -> error:: Result < crate :: Hint > {
334
+ if & bytes[ 0 ..archive:: SIZEOF_MAGIC ] == archive:: MAGIC {
335
+ Ok ( crate :: Hint :: Archive )
336
+ } else {
337
+ let ( magic, maybe_ctx) = parse_magic_and_ctx ( bytes, 0 ) ?;
338
+ match magic {
339
+ header:: MH_CIGAM_64 | header:: MH_CIGAM | header:: MH_MAGIC_64 | header:: MH_MAGIC => {
340
+ if let Some ( ctx) = maybe_ctx {
341
+ Ok ( crate :: Hint :: Mach ( crate :: HintData {
342
+ is_lsb : ctx. le . is_little ( ) ,
343
+ is_64 : Some ( ctx. container . is_big ( ) ) ,
344
+ } ) )
345
+ } else {
346
+ Err ( error:: Error :: Malformed ( format ! (
347
+ "Correct mach magic {:#x} does not have a matching parsing context!" ,
348
+ magic
349
+ ) ) )
350
+ }
351
+ }
352
+ fat:: FAT_MAGIC => {
353
+ // should probably verify this is always Big Endian...
354
+ let narchitectures = bytes. pread_with :: < u32 > ( 4 , BE ) ? as usize ;
355
+ Ok ( crate :: Hint :: MachFat ( narchitectures) )
356
+ }
357
+ _ => Ok ( crate :: Hint :: Unknown ( bytes. pread :: < u64 > ( 0 ) ?) ) ,
358
+ }
359
+ }
360
+ }
361
+
362
+ fn extract_multi_entry ( bytes : & [ u8 ] ) -> error:: Result < SingleArch > {
363
+ if let Some ( hint_bytes) = take_hint_bytes ( bytes) {
364
+ match peek_bytes ( hint_bytes) ? {
365
+ crate :: Hint :: Mach ( _) => {
366
+ let binary = MachO :: parse ( bytes, 0 ) ?;
367
+ Ok ( SingleArch :: MachO ( binary) )
368
+ }
369
+ crate :: Hint :: Archive => {
370
+ let archive = archive:: Archive :: parse ( bytes) ?;
371
+ Ok ( SingleArch :: Archive ( archive) )
372
+ }
373
+ _ => Err ( error:: Error :: Malformed ( format ! (
374
+ "multi-arch entry must be a Mach-O binary or an archive"
375
+ ) ) ) ,
376
+ }
377
+ } else {
378
+ Err ( error:: Error :: Malformed ( format ! ( "Object is too small" ) ) )
379
+ }
380
+ }
381
+
382
+ impl < ' a > Iterator for SingleArchIterator < ' a > {
383
+ type Item = error:: Result < SingleArch < ' a > > ;
326
384
fn next ( & mut self ) -> Option < Self :: Item > {
327
385
if self . index >= self . narches {
328
386
None
@@ -333,8 +391,7 @@ impl<'a> Iterator for MachOIterator<'a> {
333
391
match self . data . pread_with :: < fat:: FatArch > ( offset, scroll:: BE ) {
334
392
Ok ( arch) => {
335
393
let bytes = arch. slice ( self . data ) ;
336
- let binary = MachO :: parse ( bytes, 0 ) ;
337
- Some ( binary)
394
+ Some ( extract_multi_entry ( bytes) )
338
395
}
339
396
Err ( e) => Some ( Err ( e. into ( ) ) ) ,
340
397
}
@@ -343,10 +400,10 @@ impl<'a> Iterator for MachOIterator<'a> {
343
400
}
344
401
345
402
impl < ' a , ' b > IntoIterator for & ' b MultiArch < ' a > {
346
- type Item = error:: Result < MachO < ' a > > ;
347
- type IntoIter = MachOIterator < ' a > ;
403
+ type Item = error:: Result < SingleArch < ' a > > ;
404
+ type IntoIter = SingleArchIterator < ' a > ;
348
405
fn into_iter ( self ) -> Self :: IntoIter {
349
- MachOIterator {
406
+ SingleArchIterator {
350
407
index : 0 ,
351
408
data : self . data ,
352
409
narches : self . narches ,
@@ -387,7 +444,7 @@ impl<'a> MultiArch<'a> {
387
444
Ok ( arches)
388
445
}
389
446
/// Try to get the Mach-o binary at `index`
390
- pub fn get ( & self , index : usize ) -> error:: Result < MachO < ' a > > {
447
+ pub fn get ( & self , index : usize ) -> error:: Result < SingleArch < ' a > > {
391
448
if index >= self . narches {
392
449
return Err ( error:: Error :: Malformed ( format ! (
393
450
"Requested the {}-th binary, but there are only {} architectures in this container" ,
@@ -397,13 +454,13 @@ impl<'a> MultiArch<'a> {
397
454
let offset = ( index * fat:: SIZEOF_FAT_ARCH ) + self . start ;
398
455
let arch = self . data . pread_with :: < fat:: FatArch > ( offset, scroll:: BE ) ?;
399
456
let bytes = arch. slice ( self . data ) ;
400
- Ok ( MachO :: parse ( bytes, 0 ) ? )
457
+ extract_multi_entry ( bytes)
401
458
}
402
459
403
460
pub fn find < F : Fn ( error:: Result < fat:: FatArch > ) -> bool > (
404
461
& ' a self ,
405
462
f : F ,
406
- ) -> Option < error:: Result < MachO < ' a > > > {
463
+ ) -> Option < error:: Result < SingleArch < ' a > > > {
407
464
for ( i, arch) in self . iter_arches ( ) . enumerate ( ) {
408
465
if f ( arch) {
409
466
return Some ( self . get ( i) ) ;
@@ -464,3 +521,66 @@ impl<'a> Mach<'a> {
464
521
}
465
522
}
466
523
}
524
+
525
+ #[ cfg( test) ]
526
+ mod test {
527
+ use super :: { Mach , SingleArch } ;
528
+
529
+ #[ test]
530
+ fn parse_multi_arch_of_macho_binaries ( ) {
531
+ // Create via:
532
+ // clang -arch arm64 -shared -o /tmp/hello_world_arm hello_world.c
533
+ // clang -arch x86_64 -shared -o /tmp/hello_world_x86_64 hello_world.c
534
+ // lipo -create -output hello_world_fat_binaries /tmp/hello_world_arm /tmp/hello_world_x86_64
535
+ // strip hello_world_fat_binaries
536
+ let bytes = include_bytes ! ( concat!(
537
+ env!( "CARGO_MANIFEST_DIR" ) ,
538
+ "/assets/hello_world_fat_binaries"
539
+ ) ) ;
540
+ let mach = Mach :: parse ( bytes) . expect ( "failed to parse input file" ) ;
541
+ match mach {
542
+ Mach :: Fat ( fat) => {
543
+ assert ! ( fat. into_iter( ) . count( ) > 0 ) ;
544
+ for entry in fat. into_iter ( ) {
545
+ let entry = entry. expect ( "failed to read entry" ) ;
546
+ match entry {
547
+ SingleArch :: MachO ( macho) => {
548
+ assert ! ( macho. symbols( ) . count( ) > 0 ) ;
549
+ }
550
+ _ => panic ! ( "expected MultiArchEntry::MachO, got {:?}" , entry) ,
551
+ }
552
+ }
553
+ }
554
+ Mach :: Binary ( _) => panic ! ( "expected Mach::Fat, got Mach::Binary" ) ,
555
+ }
556
+ }
557
+
558
+ #[ test]
559
+ fn parse_multi_arch_of_archives ( ) {
560
+ // Created with:
561
+ // clang -c -o /tmp/hello_world.o hello_world.c
562
+ // ar -r /tmp/hello_world.a /tmp/hello_world.o
563
+ // lipo -create -output hello_world_fat_archives /tmp/hello_world.a
564
+ // strip hello_world_fat_archives
565
+ let bytes = include_bytes ! ( concat!(
566
+ env!( "CARGO_MANIFEST_DIR" ) ,
567
+ "/assets/hello_world_fat_archives"
568
+ ) ) ;
569
+ let mach = Mach :: parse ( bytes) . expect ( "failed to parse input file" ) ;
570
+ match mach {
571
+ Mach :: Fat ( fat) => {
572
+ assert ! ( fat. into_iter( ) . count( ) > 0 ) ;
573
+ for entry in fat. into_iter ( ) {
574
+ let entry = entry. expect ( "failed to read entry" ) ;
575
+ match entry {
576
+ SingleArch :: Archive ( archive) => {
577
+ assert ! ( !archive. members( ) . is_empty( ) )
578
+ }
579
+ _ => panic ! ( "expected MultiArchEntry::Archive, got {:?}" , entry) ,
580
+ }
581
+ }
582
+ }
583
+ Mach :: Binary ( _) => panic ! ( "expected Mach::Fat, got Mach::Binary" ) ,
584
+ }
585
+ }
586
+ }
0 commit comments