]>
git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/macho++.cpp
32836ab0cbc0ee107171f9cc9235f0141343d22c
   2  * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * This file contains Original Code and/or Modifications of Original Code 
   7  * as defined in and that are subject to the Apple Public Source License 
   8  * Version 2.0 (the 'License'). You may not use this file except in 
   9  * compliance with the License. Please obtain a copy of the License at 
  10  * http://www.opensource.apple.com/apsl/ and read it before using this 
  13  * The Original Code and all software distributed under the License are 
  14  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  15  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  16  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  17  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  18  * Please see the License for the specific language governing rights and 
  19  * limitations under the License. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  25 // macho++ - Mach-O object file helpers 
  28 #include <security_utilities/alloc.h> 
  29 #include <security_utilities/memutils.h> 
  30 #include <security_utilities/endian.h> 
  31 #include <mach-o/dyld.h> 
  38 /* Maximum number of archs a fat binary can have */ 
  39 static const int MAX_ARCH_COUNT 
= 100; 
  40 /* Maximum power of 2 that a mach-o can be aligned by */ 
  41 static const int MAX_ALIGN 
= 30; 
  44 // Architecture values 
  46 Architecture::Architecture(const fat_arch 
&arch
) 
  47         : pair
<cpu_type_t
, cpu_subtype_t
>(arch
.cputype
, arch
.cpusubtype
) 
  51 Architecture::Architecture(const char *name
) 
  53         if (const NXArchInfo 
*nxa 
= NXGetArchInfoFromName(name
)) { 
  54                 this->first 
= nxa
->cputype
; 
  55                 this->second 
= nxa
->cpusubtype
; 
  57                 this->first 
= this->second 
= none
; 
  63 // The local architecture. 
  65 // We take this from ourselves - the architecture of our main program Mach-O binary. 
  66 // There's the NXGetLocalArchInfo API, but it insists on saying "i386" on modern 
  67 // x86_64-centric systems, and lies to ppc (Rosetta) programs claiming they're native ppc. 
  68 // So let's not use that. 
  70 Architecture 
Architecture::local() 
  72         return MainMachOImage().architecture(); 
  77 // Translate between names and numbers 
  79 const char *Architecture::name() const 
  81         if (const NXArchInfo 
*info 
= NXGetArchInfoFromCpuType(cpuType(), cpuSubtype())) 
  87 std::string 
Architecture::displayName() const 
  89         if (const char *s 
= this->name()) 
  92         snprintf(buf
, sizeof(buf
), "(%d:%d)", cpuType(), cpuSubtype()); 
  98 // Compare architectures. 
  99 // This is asymmetrical; the second argument provides for some templating. 
 101 bool Architecture::matches(const Architecture 
&templ
) const 
 103         if (first 
!= templ
.first
) 
 104                 return false;   // main architecture mismatch 
 105         if (templ
.second 
== CPU_SUBTYPE_MULTIPLE
) 
 106                 return true;    // subtype wildcard 
 107         // match subtypes, ignoring feature bits 
 108         return ((second 
^ templ
.second
) & ~CPU_SUBTYPE_MASK
) == 0; 
 113 // MachOBase contains knowledge of the Mach-O object file format, 
 114 // but abstracts from any particular sourcing. It must be subclassed, 
 115 // and the subclass must provide the file header and commands area 
 116 // during its construction. Memory is owned by the subclass. 
 118 MachOBase::~MachOBase() 
 121 // provide the Mach-O file header, somehow 
 122 void MachOBase::initHeader(const mach_header 
*header
) 
 125         switch (mHeader
->magic
) { 
 143                 secinfo("macho", "%p: unrecognized header magic (%x)", this, mHeader
->magic
); 
 144                 UnixError::throwMe(ENOEXEC
); 
 148 // provide the Mach-O commands section, somehow 
 149 void MachOBase::initCommands(const load_command 
*commands
) 
 151         mCommands 
= commands
; 
 152         mEndCommands 
= LowLevelMemoryUtilities::increment
<load_command
>(commands
, flip(mHeader
->sizeofcmds
)); 
 153         if (mCommands 
+ 1 > mEndCommands
)       // ensure initial load command core available 
 154                 UnixError::throwMe(ENOEXEC
); 
 158 size_t MachOBase::headerSize() const 
 160         return m64 
? sizeof(mach_header_64
) : sizeof(mach_header
); 
 163 size_t MachOBase::commandSize() const 
 165         return flip(mHeader
->sizeofcmds
); 
 170 // Create a MachO object from an open file and a starting offset. 
 171 // We load (only) the header and load commands into memory at that time. 
 172 // Note that the offset must be relative to the start of the containing file 
 173 // (not relative to some intermediate container). 
 175 MachO::MachO(FileDesc fd
, size_t offset
, size_t length
) 
 176         : FileDesc(fd
), mOffset(offset
), mLength(length
), mSuspicious(false) 
 179                 mLength 
= fd
.fileSize(); 
 180         size_t size 
= fd
.read(&mHeaderBuffer
, sizeof(mHeaderBuffer
), mOffset
); 
 181         if (size 
!= sizeof(mHeaderBuffer
)) 
 182                 UnixError::throwMe(ENOEXEC
); 
 183         this->initHeader(&mHeaderBuffer
); 
 184         size_t cmdSize 
= this->commandSize(); 
 185         mCommandBuffer 
= (load_command 
*)malloc(cmdSize
); 
 187                 UnixError::throwMe(); 
 188         if (fd
.read(mCommandBuffer
, cmdSize
, this->headerSize() + mOffset
) != cmdSize
) 
 189                 UnixError::throwMe(ENOEXEC
); 
 190         this->initCommands(mCommandBuffer
); 
 191         /* If we do not know the length, we cannot do a verification of the mach-o structure */ 
 193                 this->validateStructure(); 
 196 void MachO::validateStructure() 
 198         bool isValid 
= false; 
 200         /* There should be either an LC_SEGMENT, an LC_SEGMENT_64, or an LC_SYMTAB 
 201          load_command and that + size must be equal to the end of the arch */ 
 202         for (const struct load_command 
*cmd 
= loadCommands(); cmd 
!= NULL
; cmd 
= nextCommand(cmd
)) { 
 203                 uint32_t cmd_type 
= flip(cmd
->cmd
); 
 204                 struct segment_command 
*seg 
= NULL
; 
 205                 struct segment_command_64 
*seg64 
= NULL
; 
 206                 struct symtab_command 
*symtab 
= NULL
; 
 208                 if (cmd_type 
==  LC_SEGMENT
) { 
 209                         if(flip(cmd
->cmdsize
) < sizeof(struct segment_command
)) { 
 210                                 UnixError::throwMe(ENOEXEC
); 
 212                         seg 
= (struct segment_command 
*)cmd
; 
 213                         if (strncmp(seg
->segname
, SEG_LINKEDIT
, sizeof(seg
->segname
)) == 0) { 
 214                                 isValid 
= flip(seg
->fileoff
) + flip(seg
->filesize
) == this->length(); 
 217                 } else if (cmd_type 
== LC_SEGMENT_64
) { 
 218                         if(flip(cmd
->cmdsize
) < sizeof(struct segment_command_64
)) { 
 219                                 UnixError::throwMe(ENOEXEC
); 
 221                         seg64 
= (struct segment_command_64 
*)cmd
; 
 222                         if (strncmp(seg64
->segname
, SEG_LINKEDIT
, sizeof(seg64
->segname
)) == 0) { 
 223                                 isValid 
= flip(seg64
->fileoff
) + flip(seg64
->filesize
) == this->length(); 
 226                 /* PPC binaries have a SYMTAB section */ 
 227                 } else if (cmd_type 
== LC_SYMTAB
) { 
 228                         if(flip(cmd
->cmdsize
) < sizeof(struct symtab_command
)) { 
 229                                 UnixError::throwMe(ENOEXEC
); 
 231                         symtab 
= (struct symtab_command 
*)cmd
; 
 232                         isValid 
= flip(symtab
->stroff
) + flip(symtab
->strsize
) == this->length(); 
 243         ::free(mCommandBuffer
); 
 248 // Create a MachO object that is (entirely) mapped into memory. 
 249 // The caller must ensire that the underlying mapping persists 
 250 // at least as long as our object. 
 252 MachOImage::MachOImage(const void *address
) 
 254         this->initHeader((const mach_header 
*)address
); 
 255         this->initCommands(LowLevelMemoryUtilities::increment
<const load_command
>(address
, this->headerSize())); 
 260 // Locate the Mach-O image of the main program 
 262 MainMachOImage::MainMachOImage() 
 263         : MachOImage(mainImageAddress()) 
 267 const void *MainMachOImage::mainImageAddress() 
 269         return _dyld_get_image_header(0); 
 274 // Return various header fields 
 276 Architecture 
MachOBase::architecture() const 
 278         return Architecture(flip(mHeader
->cputype
), flip(mHeader
->cpusubtype
)); 
 281 uint32_t MachOBase::type() const 
 283         return flip(mHeader
->filetype
); 
 286 uint32_t MachOBase::flags() const 
 288         return flip(mHeader
->flags
); 
 293 // Iterate through load commands 
 295 const load_command 
*MachOBase::nextCommand(const load_command 
*command
) const 
 297         using LowLevelMemoryUtilities::increment
; 
 298         /* Do not try and increment by 0, or it will loop forever */ 
 299         if (flip(command
->cmdsize
) == 0) 
 300                 UnixError::throwMe(ENOEXEC
); 
 301         command 
= increment
<const load_command
>(command
, flip(command
->cmdsize
)); 
 302         if (command 
>= mEndCommands
)    // end of load commands 
 304         if (increment(command
, sizeof(load_command
)) > mEndCommands
 
 305                 || increment(command
, flip(command
->cmdsize
)) > mEndCommands
) 
 306                 UnixError::throwMe(ENOEXEC
); 
 312 // Find a specific load command, by command number. 
 313 // If there are multiples, returns the first one found. 
 315 const load_command 
*MachOBase::findCommand(uint32_t cmd
) const 
 317         for (const load_command 
*command 
= loadCommands(); command
; command 
= nextCommand(command
)) 
 318                 if (flip(command
->cmd
) == cmd
) 
 325 // Locate a segment command, by name 
 327 const segment_command 
*MachOBase::findSegment(const char *segname
) const 
 329         for (const load_command 
*command 
= loadCommands(); command
; command 
= nextCommand(command
)) { 
 330                 switch (flip(command
->cmd
)) { 
 334                                 if(flip(command
->cmdsize
) < sizeof(struct segment_command
)) { 
 335                                         UnixError::throwMe(ENOEXEC
); 
 337                                 const segment_command 
*seg 
= reinterpret_cast<const segment_command 
*>(command
); 
 338                                 if (!strncmp(seg
->segname
, segname
, sizeof(seg
->segname
))) 
 349 const section 
*MachOBase::findSection(const char *segname
, const char *sectname
) const 
 351         using LowLevelMemoryUtilities::increment
; 
 352         if (const segment_command 
*seg 
= findSegment(segname
)) { 
 354                         if(flip(seg
->cmdsize
) < sizeof(segment_command_64
)) { 
 355                                 UnixError::throwMe(ENOEXEC
); 
 357                         const segment_command_64 
*seg64 
= reinterpret_cast<const segment_command_64 
*>(seg
); 
 358                         if (sizeof(*seg64
) + (seg64
->nsects 
* sizeof(section_64
)) > flip(seg64
->cmdsize
))               // too many segments; doesn't fit (malformed Mach-O) 
 360                         const section_64 
*sect 
= increment
<const section_64
>(seg64 
+ 1, 0); 
 361                         for (unsigned n 
= flip(seg64
->nsects
); n 
> 0; n
--, sect
++) { 
 362                                 if (!strncmp(sect
->sectname
, sectname
, sizeof(sect
->sectname
))) 
 363                                         return reinterpret_cast<const section 
*>(sect
); 
 366                         if (sizeof(*seg
) + (seg
->nsects 
* sizeof(section
)) > flip(seg
->cmdsize
))                // too many segments; doesn't fit (malformed Mach-O) 
 368                         const section 
*sect 
= increment
<const section
>(seg 
+ 1, 0); 
 369                         for (unsigned n 
= flip(seg
->nsects
); n 
> 0; n
--, sect
++) { 
 370                                 if (!strncmp(sect
->sectname
, sectname
, sizeof(sect
->sectname
))) 
 380 // Translate a union lc_str into the string it denotes. 
 381 // Returns NULL (no exceptions) if the entry is corrupt. 
 383 const char *MachOBase::string(const load_command 
*cmd
, const lc_str 
&str
) const 
 385         size_t offset 
= flip(str
.offset
); 
 386         const char *sp 
= LowLevelMemoryUtilities::increment
<const char>(cmd
, offset
); 
 387         if (offset 
+ strlen(sp
) + 1 > flip(cmd
->cmdsize
))       // corrupt string reference 
 394 // Figure out where the Code Signing information starts in the Mach-O binary image. 
 395 // The code signature is at the end of the file, and identified 
 396 // by a specially-named section. So its starting offset is also the end 
 397 // of the signable part. 
 398 // Note that the offset returned is relative to the start of the Mach-O image. 
 399 // Returns zero if not found (usually indicating that the binary was not signed). 
 401 const linkedit_data_command 
*MachOBase::findCodeSignature() const 
 403         if (const load_command 
*cmd 
= findCommand(LC_CODE_SIGNATURE
)) { 
 404                 if(flip(cmd
->cmdsize
) < sizeof(linkedit_data_command
)) { 
 405                         UnixError::throwMe(ENOEXEC
); 
 407                 return reinterpret_cast<const linkedit_data_command 
*>(cmd
); 
 409         return NULL
;            // not found 
 412 size_t MachOBase::signingOffset() const 
 414         if (const linkedit_data_command 
*lec 
= findCodeSignature()) 
 415                 return flip(lec
->dataoff
); 
 420 size_t MachOBase::signingLength() const 
 422         if (const linkedit_data_command 
*lec 
= findCodeSignature()) 
 423                 return flip(lec
->datasize
); 
 428 const linkedit_data_command 
*MachOBase::findLibraryDependencies() const 
 430         if (const load_command 
*cmd 
= findCommand(LC_DYLIB_CODE_SIGN_DRS
)) { 
 431                 if(flip(cmd
->cmdsize
) < sizeof(linkedit_data_command
)) { 
 432                         UnixError::throwMe(ENOEXEC
); 
 434                 return reinterpret_cast<const linkedit_data_command 
*>(cmd
); 
 436         return NULL
;            // not found 
 439 const version_min_command 
*MachOBase::findMinVersion() const 
 441         for (const load_command 
*command 
= loadCommands(); command
; command 
= nextCommand(command
)) 
 442                 switch (flip(command
->cmd
)) { 
 443                 case LC_VERSION_MIN_MACOSX
: 
 444                 case LC_VERSION_MIN_IPHONEOS
: 
 445                 case LC_VERSION_MIN_WATCHOS
: 
 446                 case LC_VERSION_MIN_TVOS
: 
 447                         if(flip(command
->cmdsize
) < sizeof(version_min_command
)) { 
 448                                 UnixError::throwMe(ENOEXEC
); 
 450                         return reinterpret_cast<const version_min_command 
*>(command
); 
 455 const build_version_command 
*MachOBase::findBuildVersion() const 
 457     for (const load_command 
*command 
= loadCommands(); command
; command 
= nextCommand(command
)) { 
 458         if (flip(command
->cmd
) == LC_BUILD_VERSION
) { 
 459             if(flip(command
->cmdsize
) < sizeof(build_version_command
)) { 
 460                 UnixError::throwMe(ENOEXEC
); 
 463             return reinterpret_cast<const build_version_command 
*>(command
); 
 469 bool MachOBase::version(uint32_t *platform
, uint32_t *minVersion
, uint32_t *sdkVersion
) const 
 471     const build_version_command 
*bc 
= findBuildVersion(); 
 474         if (platform 
!= NULL
) { *platform 
= flip(bc
->platform
); } 
 475         if (minVersion 
!= NULL
) { *minVersion 
= flip(bc
->minos
); } 
 476         if (sdkVersion 
!= NULL
) { *sdkVersion 
= flip(bc
->sdk
); } 
 480     const version_min_command 
*vc 
= findMinVersion(); 
 484         switch (flip(vc
->cmd
)) { 
 485         case LC_VERSION_MIN_MACOSX
: 
 488         case LC_VERSION_MIN_IPHONEOS
: 
 491         case LC_VERSION_MIN_WATCHOS
: 
 492             pf 
= PLATFORM_WATCHOS
; 
 494         case LC_VERSION_MIN_TVOS
: 
 498             // Old style load command, but we don't know what platform to map to. 
 502         if (platform 
!= NULL
) { *platform 
= pf
; } 
 503         if (minVersion 
!= NULL
) { *minVersion 
= flip(vc
->version
); } 
 504         if (sdkVersion 
!= NULL
) { *sdkVersion 
= flip(vc
->sdk
); } 
 512 // Return the signing-limit length for this Mach-O binary image. 
 513 // This is the signingOffset if present, or the full length if not. 
 515 size_t MachO::signingExtent() const 
 517         if (size_t offset 
= signingOffset()) 
 527 void MachO::seek(size_t offset
) 
 529         FileDesc::seek(mOffset 
+ offset
); 
 532 CFDataRef 
MachO::dataAt(size_t offset
, size_t size
) 
 534         CFMallocData 
buffer(size
); 
 535         if (this->read(buffer
, size
, mOffset 
+ offset
) != size
) 
 536                 UnixError::throwMe(); 
 541 // Fat (aka universal) file wrappers. 
 542 // The offset is relative to the start of the containing file. 
 544 Universal::Universal(FileDesc fd
, size_t offset 
/* = 0 */, size_t length 
/* = 0 */) 
 545         : FileDesc(fd
), mBase(offset
), mLength(length
), mMachType(0), mSuspicious(false) 
 548                 fat_header header
;              // if this is a fat file 
 549                 mach_header mheader
;    // if this is a thin file 
 552         if (fd
.read(&unionHeader
, sizeof(unionHeader
), offset
) != sizeof(unionHeader
)) 
 553                 UnixError::throwMe(ENOEXEC
); 
 554         switch (unionHeader
.header
.magic
) { 
 560                         // Under certain circumstances (15001604), mArchCount under-counts the architectures 
 561                         // by one, and special testing is required to validate the extra-curricular entry. 
 562                         // We always read an extra entry; in the situations where this might hit end-of-file, 
 563                         // we are content to fail. 
 565                         mArchCount 
= ntohl(unionHeader
.header
.nfat_arch
); 
 567                         if (mArchCount 
> MAX_ARCH_COUNT
) 
 568                                 UnixError::throwMe(ENOEXEC
); 
 570                         size_t archSize 
= sizeof(fat_arch
) * (mArchCount 
+ 1); 
 571                         mArchList 
= (fat_arch 
*)malloc(archSize
); 
 573                                 UnixError::throwMe(); 
 574                         if (fd
.read(mArchList
, archSize
, mBase 
+ sizeof(unionHeader
.header
)) != archSize
) { 
 576                                 UnixError::throwMe(ENOEXEC
); 
 578                         for (fat_arch 
*arch 
= mArchList
; arch 
<= mArchList 
+ mArchCount
; arch
++) { 
 580                                 n2hi(arch
->cpusubtype
); 
 585                         const fat_arch 
*last_arch 
= mArchList 
+ mArchCount
; 
 586                         if (last_arch
->cputype 
== (CPU_ARCH_ABI64 
| CPU_TYPE_ARM
)) { 
 589                         secinfo("macho", "%p is a fat file with %d architectures", 
 592                         /* A Mach-O universal file has padding of no more than "page size" 
 593                          * between the header and slices. This padding must be zeroed out or the file 
 595                         std::list
<struct fat_arch 
*> sortedList
; 
 596                         for (unsigned i 
= 0; i 
< mArchCount
; i
++) 
 597                                 sortedList
.push_back(mArchList 
+ i
); 
 599                         sortedList
.sort(^ bool (const struct fat_arch 
*arch1
, const struct fat_arch 
*arch2
) { return arch1
->offset 
< arch2
->offset
; }); 
 601                         const size_t universalHeaderEnd 
= mBase 
+ sizeof(unionHeader
.header
) + (sizeof(fat_arch
) * mArchCount
); 
 602                         size_t prevHeaderEnd 
= universalHeaderEnd
; 
 603                         size_t prevArchSize 
= 0, prevArchStart 
= 0; 
 605                         for (auto iterator 
= sortedList
.begin(); iterator 
!= sortedList
.end(); ++iterator
) { 
 606                                 auto ret 
= mSizes
.insert(std::pair
<size_t, size_t>((*iterator
)->offset
, (*iterator
)->size
)); 
 607                                 if (ret
.second 
== false) { 
 609                                         MacOSError::throwMe(errSecInternalError
); // Something is wrong if the same size was encountered twice 
 612                                 size_t gapSize 
= (*iterator
)->offset 
- prevHeaderEnd
; 
 614                                 /* The size of the padding after the universal cannot be calculated to a fixed size */ 
 615                                 if (prevHeaderEnd 
!= universalHeaderEnd
) { 
 616                                         if (((*iterator
)->align 
> MAX_ALIGN
) || gapSize 
>= (1 << (*iterator
)->align
)) { 
 622                                 // validate gap bytes in tasty page-sized chunks 
 623                                 CssmAutoPtr
<uint8_t> gapBytes(Allocator::standard().malloc
<uint8_t>(PAGE_SIZE
)); 
 625                                 while (off 
< gapSize
) { 
 626                                         size_t want 
= min(gapSize 
- off
, (size_t)PAGE_SIZE
); 
 627                                         size_t got 
= fd
.read(gapBytes
, want
, prevHeaderEnd 
+ off
); 
 633                                         for (size_t x 
= 0; x 
< got
; x
++) { 
 634                                                 if (gapBytes
[x
] != 0) { 
 647                                 prevHeaderEnd 
= (*iterator
)->offset 
+ (*iterator
)->size
; 
 648                                 prevArchSize 
= (*iterator
)->size
; 
 649                                 prevArchStart 
= (*iterator
)->offset
; 
 652                         /* If there is anything extra at the end of the file, reject this */ 
 653                         if (!mSuspicious 
&& (prevArchStart 
+ prevArchSize 
!= fd
.fileSize())) 
 662                 mThinArch 
= Architecture(unionHeader
.mheader
.cputype
, unionHeader
.mheader
.cpusubtype
); 
 663                 secinfo("macho", "%p is a thin file (%s)", this, mThinArch
.name()); 
 669                 mThinArch 
= Architecture(flip(unionHeader
.mheader
.cputype
), flip(unionHeader
.mheader
.cpusubtype
)); 
 670                 secinfo("macho", "%p is a thin file (%s)", this, mThinArch
.name()); 
 673                 UnixError::throwMe(ENOEXEC
); 
 677 Universal::~Universal() 
 682 size_t Universal::lengthOfSlice(size_t offset
) const 
 684         auto ret 
= mSizes
.find(offset
); 
 685         if (ret 
== mSizes
.end()) 
 686                 MacOSError::throwMe(errSecInternalError
); 
 691 // Get the "local" architecture from the fat file 
 692 // Throws ENOEXEC if not found. 
 694 MachO 
*Universal::architecture() const 
 697                 return findImage(bestNativeArch()); 
 699                 return new MachO(*this, mBase
, mLength
); 
 702 size_t Universal::archOffset() const 
 705                 return mBase 
+ findArch(bestNativeArch())->offset
; 
 712 // Get the specified architecture from the fat file 
 713 // Throws ENOEXEC if not found. 
 715 MachO 
*Universal::architecture(const Architecture 
&arch
) const 
 718                 return findImage(arch
); 
 719         else if (mThinArch
.matches(arch
)) 
 720                 return new MachO(*this, mBase
); 
 722                 UnixError::throwMe(ENOEXEC
); 
 725 size_t Universal::archOffset(const Architecture 
&arch
) const 
 728                 return mBase 
+ findArch(arch
)->offset
; 
 729         else if (mThinArch
.matches(arch
)) 
 732                 UnixError::throwMe(ENOEXEC
); 
 735 size_t Universal::archLength(const Architecture 
&arch
) const 
 738                 return mBase 
+ findArch(arch
)->size
; 
 739         else if (mThinArch
.matches(arch
)) 
 740                 return this->fileSize(); 
 742                 UnixError::throwMe(ENOEXEC
); 
 746 // Get the architecture at a specified offset from the fat file. 
 747 // Throws an exception of the offset does not point at a Mach-O image. 
 749 MachO 
*Universal::architecture(size_t offset
) const 
 752                 return make(new MachO(*this, offset
)); 
 753         else if (offset 
== mBase
) 
 754                 return new MachO(*this); 
 756                 UnixError::throwMe(ENOEXEC
); 
 761 // Locate an architecture from the fat file's list. 
 762 // Throws ENOEXEC if not found. 
 764 const fat_arch 
*Universal::findArch(const Architecture 
&target
) const 
 766         assert(isUniversal()); 
 767         const fat_arch 
*end 
= mArchList 
+ mArchCount
; 
 769         for (const fat_arch 
*arch 
= mArchList
; arch 
< end
; ++arch
) 
 770                 if (arch
->cputype 
== target
.cpuType() 
 771                         && arch
->cpusubtype 
== target
.cpuSubtype()) 
 773         // match for generic model of main architecture 
 774         for (const fat_arch 
*arch 
= mArchList
; arch 
< end
; ++arch
) 
 775                 if (arch
->cputype 
== target
.cpuType() && arch
->cpusubtype 
== 0) 
 777         // match for any subarchitecture of the main architecture (questionable) 
 778         for (const fat_arch 
*arch 
= mArchList
; arch 
< end
; ++arch
) 
 779                 if (arch
->cputype 
== target
.cpuType()) 
 782         UnixError::throwMe(ENOEXEC
);    // not found     
 785 MachO 
*Universal::findImage(const Architecture 
&target
) const 
 787         const fat_arch 
*arch 
= findArch(target
); 
 788         return make(new MachO(*this, mBase 
+ arch
->offset
, arch
->size
)); 
 791 MachO
* Universal::make(MachO
* macho
) const 
 793         auto_ptr
<MachO
> mo(macho
);                              // safe resource 
 794         uint32_t type 
= mo
->type(); 
 795         if (type 
== 0)                                                  // not a recognized Mach-O type 
 796                 UnixError::throwMe(ENOEXEC
); 
 797         if (mMachType 
&& mMachType 
!= type
)             // inconsistent members 
 798                 UnixError::throwMe(ENOEXEC
); 
 799         mMachType 
= type
;                                               // record 
 805 // Find the best-matching architecture for this fat file. 
 806 // We pick the native architecture if it's available. 
 807 // If it contains exactly one architecture, we take that. 
 808 // Otherwise, we throw. 
 810 Architecture 
Universal::bestNativeArch() const 
 813                 // ask the NXArch API for our native architecture 
 814                 const Architecture native 
= Architecture::local(); 
 815                 if (fat_arch 
*match 
= NXFindBestFatArch(native
.cpuType(), native
.cpuSubtype(), mArchList
, mArchCount
)) 
 817                 // if the system can't figure it out, pick (arbitrarily) the first one 
 824 // List all architectures from the fat file's list. 
 826 void Universal::architectures(Architectures 
&archs
) const 
 829                 for (unsigned n 
= 0; n 
< mArchCount
; n
++) 
 830                         archs
.insert(mArchList
[n
]); 
 832                 auto_ptr
<MachO
> macho(architecture()); 
 833                 archs
.insert(macho
->architecture()); 
 838 // Quickly guess the Mach-O type of a file. 
 839 // Returns type zero if the file isn't Mach-O or Universal. 
 840 // Always looks at the start of the file, and does not change the file pointer. 
 842 uint32_t Universal::typeOf(FileDesc fd
) 
 846         if (fd
.read(&header
, sizeof(header
), 0) != sizeof(header
)) 
 848         while (max_tries 
> 0) { 
 849                 switch (header
.magic
) { 
 852                         return header
.filetype
; 
 855                         return flip(header
.filetype
); 
 859                                 const fat_arch 
*arch1 
= 
 860                                         LowLevelMemoryUtilities::increment
<fat_arch
>(&header
, sizeof(fat_header
)); 
 861                                 if (fd
.read(&header
, sizeof(header
), ntohl(arch1
->offset
)) != sizeof(header
)) 
 876 bool Universal::isSuspicious() const 
 880         Universal::Architectures archList
; 
 881         architectures(archList
); 
 882         for (Universal::Architectures::const_iterator it 
= archList
.begin(); it 
!= archList
.end(); ++it
) { 
 883                 auto_ptr
<MachO
> macho(architecture(*it
)); 
 884                 if (macho
->isSuspicious())