1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-  
   3  * Copyright (c) 2006-2007 Apple Inc. All rights reserved. 
   5  * @APPLE_LICENSE_HEADER_START@ 
   7  * This file contains Original Code and/or Modifications of Original Code 
   8  * as defined in and that are subject to the Apple Public Source License 
   9  * Version 2.0 (the 'License'). You may not use this file except in 
  10  * compliance with the License. Please obtain a copy of the License at 
  11  * http://www.opensource.apple.com/apsl/ and read it before using this 
  14  * The Original Code and all software distributed under the License are 
  15  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  16  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  17  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  18  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  19  * Please see the License for the specific language governing rights and 
  20  * limitations under the License. 
  22  * @APPLE_LICENSE_HEADER_END@ 
  25 #include <sys/types.h> 
  36 #include <ext/hash_set> 
  38 #include "MachOFileAbstraction.hpp" 
  39 #include "Architectures.hpp" 
  42  __attribute__((noreturn
)) 
  43 void throwf(const char* format
, ...)  
  47         va_start(list
, format
); 
  48         vasprintf(&p
, format
, list
); 
  60         static bool                                                                     validFile(const uint8_t* fileContent
); 
  61         static MachOChecker
<A
>*                                         make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)  
  62                                                                                                                 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); } 
  63         virtual                                                                         ~MachOChecker() {} 
  67         typedef typename 
A::P                                   P
; 
  68         typedef typename 
A::P::E                                E
; 
  69         typedef typename 
A::P::uint_t                   pint_t
; 
  74                 bool operator()(const char* left
, const char* right
) const { return (strcmp(left
, right
) == 0); } 
  77         typedef __gnu_cxx::hash_set
<const char*, __gnu_cxx::hash
<const char*>, CStringEquals
>  StringSet
; 
  79                                                                                                 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
); 
  80         void                                                                            checkMachHeader(); 
  81         void                                                                            checkLoadCommands(); 
  82         void                                                                            checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
); 
  83         uint8_t                                                                         loadCommandSizeMask(); 
  84         void                                                                            checkSymbolTable(); 
  85         void                                                                            checkIndirectSymbolTable(); 
  86         void                                                                            checkRelocations(); 
  87         void                                                                            checkExternalReloation(const macho_relocation_info
<P
>* reloc
); 
  88         void                                                                            checkLocalReloation(const macho_relocation_info
<P
>* reloc
); 
  90         bool                                                                            addressInWritableSegment(pint_t address
); 
  93         const macho_header
<P
>*                                          fHeader
; 
  96         const char*                                                                     fStringsEnd
; 
  97         const macho_nlist
<P
>*                                           fSymbols
; 
  98         uint32_t                                                                        fSymbolCount
; 
  99         const macho_dysymtab_command
<P
>*                        fDynamicSymbolTable
; 
 100         const uint32_t*                                                         fIndirectTable
; 
 101         uint32_t                                                                        fIndirectTableCount
; 
 102         const macho_relocation_info
<P
>*                         fLocalRelocations
; 
 103         uint32_t                                                                        fLocalRelocationsCount
; 
 104         const macho_relocation_info
<P
>*                         fExternalRelocations
; 
 105         uint32_t                                                                        fExternalRelocationsCount
; 
 106         bool                                                                            fWriteableSegmentWithAddrOver4G
; 
 107         const macho_segment_command
<P
>*                         fFirstSegment
; 
 108         const macho_segment_command
<P
>*                         fFirstWritableSegment
; 
 109         uint32_t                                                                        fSectionCount
; 
 115 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
) 
 117         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 118         if ( header
->magic() != MH_MAGIC 
) 
 120         if ( header
->cputype() != CPU_TYPE_POWERPC 
) 
 122         switch (header
->filetype()) { 
 133 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
) 
 135         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 136         if ( header
->magic() != MH_MAGIC_64 
) 
 138         if ( header
->cputype() != CPU_TYPE_POWERPC64 
) 
 140         switch (header
->filetype()) { 
 151 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
) 
 153         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 154         if ( header
->magic() != MH_MAGIC 
) 
 156         if ( header
->cputype() != CPU_TYPE_I386 
) 
 158         switch (header
->filetype()) { 
 169 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
) 
 171         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 172         if ( header
->magic() != MH_MAGIC_64 
) 
 174         if ( header
->cputype() != CPU_TYPE_X86_64 
) 
 176         switch (header
->filetype()) { 
 187 bool MachOChecker
<arm
>::validFile(const uint8_t* fileContent
) 
 189         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 190         if ( header
->magic() != MH_MAGIC 
) 
 192         if ( header
->cputype() != CPU_TYPE_ARM 
) 
 194         switch (header
->filetype()) { 
 204 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask()    { return 0x03; } 
 205 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask()  { return 0x07; } 
 206 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask()    { return 0x03; } 
 207 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; } 
 208 template <> uint8_t MachOChecker
<arm
>::loadCommandSizeMask()    { return 0x03; } 
 210 template <typename A
> 
 211 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
) 
 212  : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fDynamicSymbolTable(NULL
), fIndirectTableCount(0), 
 213  fLocalRelocations(NULL
),  fLocalRelocationsCount(0),  fExternalRelocations(NULL
),  fExternalRelocationsCount(0), 
 214  fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
), fSectionCount(0) 
 217         if ( ! validFile(fileContent
) ) 
 218                 throw "not a mach-o file that can be checked"; 
 220         fPath 
= strdup(path
); 
 221         fHeader 
= (const macho_header
<P
>*)fileContent
; 
 223         // sanity check header 
 226         // check load commands 
 229         checkIndirectSymbolTable(); 
 237 template <typename A
> 
 238 void MachOChecker
<A
>::checkMachHeader() 
 240         if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength 
) 
 241                 throw "sizeofcmds in mach_header is larger than file"; 
 243         uint32_t flags 
= fHeader
->flags(); 
 244         const uint32_t invalidBits 
= MH_INCRLINK 
| MH_LAZY_INIT 
| 0xFFC00000; 
 245         if ( flags 
& invalidBits 
) 
 246                 throw "invalid bits in mach_header flags"; 
 247         if ( (flags 
& MH_NO_REEXPORTED_DYLIBS
) && (fHeader
->filetype() != MH_DYLIB
) )  
 248                 throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags only valid for dylibs"; 
 251 template <typename A
> 
 252 void MachOChecker
<A
>::checkLoadCommands() 
 254         // check that all load commands fit within the load command space file 
 255         const macho_encryption_info_command
<P
>* encryption_info 
= NULL
; 
 256         const uint8_t* const endOfFile 
= (uint8_t*)fHeader 
+ fLength
; 
 257         const uint8_t* const endOfLoadCommands 
= (uint8_t*)fHeader 
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds(); 
 258         const uint32_t cmd_count 
= fHeader
->ncmds(); 
 259         const macho_load_command
<P
>* const cmds 
= (macho_load_command
<P
>*)((uint8_t*)fHeader 
+ sizeof(macho_header
<P
>)); 
 260         const macho_load_command
<P
>* cmd 
= cmds
; 
 261         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 262                 uint32_t size 
= cmd
->cmdsize(); 
 263                 if ( (size 
& this->loadCommandSizeMask()) != 0 ) 
 264                         throwf("load command #%d has a unaligned size", i
); 
 265                 const uint8_t* endOfCmd 
= ((uint8_t*)cmd
)+cmd
->cmdsize(); 
 266                 if ( endOfCmd 
> endOfLoadCommands 
) 
 267                         throwf("load command #%d extends beyond the end of the load commands", i
); 
 268                 if ( endOfCmd 
> endOfFile 
) 
 269                         throwf("load command #%d extends beyond the end of the file", i
); 
 270                 switch ( cmd
->cmd()     ) { 
 271                         case macho_segment_command
<P
>::CMD
: 
 277                         case LC_LOAD_DYLINKER
: 
 279                         case macho_routines_command
<P
>::CMD
: 
 280                         case LC_SUB_FRAMEWORK
: 
 282                         case LC_TWOLEVEL_HINTS
: 
 283                         case LC_PREBIND_CKSUM
: 
 284                         case LC_LOAD_WEAK_DYLIB
: 
 285                         case LC_LAZY_LOAD_DYLIB
: 
 287                         case LC_REEXPORT_DYLIB
: 
 288                         case LC_SEGMENT_SPLIT_INFO
: 
 289                         case LC_CODE_SIGNATURE
: 
 291                         case LC_DYLD_INFO_ONLY
: 
 293                         case LC_ENCRYPTION_INFO
: 
 294                                 encryption_info 
= (macho_encryption_info_command
<P
>*)cmd
; 
 296                         case LC_SUB_UMBRELLA
: 
 298                                 if ( fHeader
->flags() & MH_NO_REEXPORTED_DYLIBS 
) 
 299                                         throw "MH_NO_REEXPORTED_DYLIBS bit of mach_header flags should not be set in an image with LC_SUB_LIBRARY or LC_SUB_UMBRELLA"; 
 302                                 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd()); 
 304                 cmd 
= (const macho_load_command
<P
>*)endOfCmd
; 
 309         std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
; 
 310         std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
; 
 311         const macho_segment_command
<P
>* linkEditSegment 
= NULL
; 
 312         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 313                 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD 
) { 
 314                         const macho_segment_command
<P
>* segCmd 
= (const macho_segment_command
<P
>*)cmd
; 
 315                         if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) ) 
 316                                 throw "invalid segment load command size"; 
 318                         // see if this overlaps another segment address range 
 319                         uint64_t startAddr 
= segCmd
->vmaddr(); 
 320                         uint64_t endAddr 
= startAddr 
+ segCmd
->vmsize(); 
 321                         for (typename 
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it 
= segmentAddressRanges
.begin(); it 
!= segmentAddressRanges
.end(); ++it
) { 
 322                                 if ( it
->first 
< startAddr 
) { 
 323                                         if ( it
->second 
> startAddr 
) 
 324                                                 throw "overlapping segment vm addresses"; 
 326                                 else if ( it
->first 
> startAddr 
) { 
 327                                         if ( it
->first 
< endAddr 
) 
 328                                                 throw "overlapping segment vm addresses"; 
 331                                         throw "overlapping segment vm addresses"; 
 333                                 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
)); 
 335                         // see if this overlaps another segment file offset range 
 336                         uint64_t startOffset 
= segCmd
->fileoff(); 
 337                         uint64_t endOffset 
= startOffset 
+ segCmd
->filesize(); 
 338                         for (typename 
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it 
= segmentFileOffsetRanges
.begin(); it 
!= segmentFileOffsetRanges
.end(); ++it
) { 
 339                                 if ( it
->first 
< startOffset 
) { 
 340                                         if ( it
->second 
> startOffset 
) 
 341                                                 throw "overlapping segment file data"; 
 343                                 else if ( it
->first 
> startOffset 
) { 
 344                                         if ( it
->first 
< endOffset 
) 
 345                                                 throw "overlapping segment file data"; 
 348                                         throw "overlapping segment file data"; 
 350                                 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
)); 
 351                                 // check is within file bounds 
 352                                 if ( (startOffset 
> fLength
) || (endOffset 
> fLength
) ) 
 353                                         throw "segment file data is past end of file"; 
 355                         // verify it fits in file 
 356                         if ( startOffset 
> fLength 
) 
 357                                 throw "segment fileoff does not fit in file"; 
 358                         if ( endOffset 
> fLength 
) 
 359                                 throw "segment fileoff+filesize does not fit in file"; 
 361                         // keep LINKEDIT segment  
 362                         if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 ) 
 363                                 linkEditSegment 
= segCmd
; 
 365                         // cache interesting segments 
 366                         if ( fFirstSegment 
== NULL 
) 
 367                                 fFirstSegment 
= segCmd
; 
 368                         if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) { 
 369                                 if ( fFirstWritableSegment 
== NULL 
) 
 370                                         fFirstWritableSegment 
= segCmd
; 
 371                                 if ( segCmd
->vmaddr() > 0x100000000ULL 
) 
 372                                         fWriteableSegmentWithAddrOver4G 
= true; 
 375                         // check section ranges 
 376                         const macho_section
<P
>* const sectionsStart 
= (macho_section
<P
>*)((char*)segCmd 
+ sizeof(macho_segment_command
<P
>)); 
 377                         const macho_section
<P
>* const sectionsEnd 
= §ionsStart
[segCmd
->nsects()]; 
 378                         for(const macho_section
<P
>* sect 
= sectionsStart
; sect 
< sectionsEnd
; ++sect
) { 
 379                                 // check all sections are within segment 
 380                                 if ( sect
->addr() < startAddr 
) 
 381                                         throwf("section %s vm address not within segment", sect
->sectname()); 
 382                                 if ( (sect
->addr()+sect
->size()) > endAddr 
) 
 383                                         throwf("section %s vm address not within segment", sect
->sectname()); 
 384                                 if ( ((sect
->flags() & SECTION_TYPE
) != S_ZEROFILL
) && (segCmd
->filesize() != 0) ) { 
 385                                         if ( sect
->offset() < startOffset 
) 
 386                                                 throwf("section %s file offset not within segment", sect
->sectname()); 
 387                                         if ( (sect
->offset()+sect
->size()) > endOffset 
) 
 388                                                 throwf("section %s file offset not within segment", sect
->sectname()); 
 390                                 checkSection(segCmd
, sect
); 
 394                 cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 397         // verify there was a LINKEDIT segment 
 398         if ( linkEditSegment 
== NULL 
) 
 399                 throw "no __LINKEDIT segment"; 
 401         // checks for executables 
 402         bool isStaticExecutable 
= false; 
 403         if ( fHeader
->filetype() == MH_EXECUTE 
) { 
 404                 isStaticExecutable 
= true; 
 406                 for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 407                         switch ( cmd
->cmd() ) { 
 408                                 case LC_LOAD_DYLINKER
: 
 409                                         // the existence of a dyld load command makes a executable dynamic 
 410                                         isStaticExecutable 
= false; 
 413                         cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 415                 if ( isStaticExecutable 
) { 
 416                         if ( fHeader
->flags() != MH_NOUNDEFS 
) 
 417                                 throw "invalid bits in mach_header flags for static executable"; 
 421         // verify encryption info 
 422         if ( encryption_info 
!= NULL 
) { 
 423                 if ( fHeader
->filetype() != MH_EXECUTE 
) 
 424                         throw "LC_ENCRYPTION_INFO load command is only legal in main executables"; 
 425                 if ( encryption_info
->cryptoff() <  (sizeof(macho_header
<P
>) + fHeader
->sizeofcmds()) ) 
 426                         throw "LC_ENCRYPTION_INFO load command has cryptoff covers some load commands"; 
 427                 if ( (encryption_info
->cryptoff() % 4096) != 0 ) 
 428                         throw "LC_ENCRYPTION_INFO load command has cryptoff which is not page aligned"; 
 429                 if ( (encryption_info
->cryptsize() % 4096) != 0 ) 
 430                         throw "LC_ENCRYPTION_INFO load command has cryptsize which is not page sized"; 
 431                 for (typename 
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it 
= segmentFileOffsetRanges
.begin();  
 432                                                                                                                                 it 
!= segmentFileOffsetRanges
.end(); ++it
) { 
 433                         if ( (it
->first 
<= encryption_info
->cryptoff()) && (encryption_info
->cryptoff() < it
->second
) ) { 
 434                                 if ( (encryption_info
->cryptoff() + encryption_info
->cryptsize()) > it
->second 
) 
 435                                         throw "LC_ENCRYPTION_INFO load command is not contained within one segment"; 
 440         // check LC_SYMTAB, LC_DYSYMTAB, and LC_SEGMENT_SPLIT_INFO 
 442         bool foundDynamicSymTab 
= false; 
 443         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 444                 switch ( cmd
->cmd() ) { 
 447                                         const macho_symtab_command
<P
>* symtab 
= (macho_symtab_command
<P
>*)cmd
; 
 448                                         fSymbolCount 
= symtab
->nsyms(); 
 449                                         fSymbols 
= (const macho_nlist
<P
>*)((char*)fHeader 
+ symtab
->symoff()); 
 450                                         if ( symtab
->symoff() < linkEditSegment
->fileoff() ) 
 451                                                 throw "symbol table not in __LINKEDIT"; 
 452                                         if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 453                                                 throw "symbol table end not in __LINKEDIT"; 
 454                                         if ( (symtab
->symoff() % sizeof(pint_t
)) != 0 ) 
 455                                                 throw "symbol table start not pointer aligned"; 
 456                                         fStrings 
= (char*)fHeader 
+ symtab
->stroff(); 
 457                                         fStringsEnd 
= fStrings 
+ symtab
->strsize(); 
 458                                         if ( symtab
->stroff() < linkEditSegment
->fileoff() ) 
 459                                                 throw "string pool not in __LINKEDIT"; 
 460                                         if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 461                                                 throw "string pool extends beyond __LINKEDIT"; 
 462                                         if ( (symtab
->stroff() % 4) != 0 ) // work around until rdar://problem/4737991 is fixed 
 463                                                 throw "string pool start not pointer aligned"; 
 464                                         if ( (symtab
->strsize() % sizeof(pint_t
)) != 0 )         
 465                                                 throw "string pool size not a multiple of pointer size"; 
 470                                         if ( isStaticExecutable 
) 
 471                                                 throw "LC_DYSYMTAB should not be used in static executable"; 
 472                                         foundDynamicSymTab 
= true; 
 473                                         fDynamicSymbolTable 
= (struct macho_dysymtab_command
<P
>*)cmd
; 
 474                                         fIndirectTable 
= (uint32_t*)((char*)fHeader 
+ fDynamicSymbolTable
->indirectsymoff()); 
 475                                         fIndirectTableCount 
= fDynamicSymbolTable
->nindirectsyms(); 
 476                                         if ( fIndirectTableCount 
!= 0  ) { 
 477                                                 if ( fDynamicSymbolTable
->indirectsymoff() < linkEditSegment
->fileoff() ) 
 478                                                         throw "indirect symbol table not in __LINKEDIT"; 
 479                                                 if ( (fDynamicSymbolTable
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 480                                                         throw "indirect symbol table not in __LINKEDIT"; 
 481                                                 if ( (fDynamicSymbolTable
->indirectsymoff() % sizeof(pint_t
)) != 0 ) 
 482                                                         throw "indirect symbol table not pointer aligned"; 
 484                                         fLocalRelocationsCount 
= fDynamicSymbolTable
->nlocrel(); 
 485                                         if ( fLocalRelocationsCount 
!= 0 ) { 
 486                                                 fLocalRelocations 
= (const macho_relocation_info
<P
>*)((char*)fHeader 
+ fDynamicSymbolTable
->locreloff()); 
 487                                                 if ( fDynamicSymbolTable
->locreloff() < linkEditSegment
->fileoff() ) 
 488                                                         throw "local relocations not in __LINKEDIT"; 
 489                                                 if ( (fDynamicSymbolTable
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 490                                                         throw "local relocations not in __LINKEDIT"; 
 491                                                 if ( (fDynamicSymbolTable
->locreloff() % sizeof(pint_t
)) != 0 ) 
 492                                                         throw "local relocations table not pointer aligned"; 
 494                                         fExternalRelocationsCount 
= fDynamicSymbolTable
->nextrel(); 
 495                                         if ( fExternalRelocationsCount 
!= 0 ) { 
 496                                                 fExternalRelocations 
= (const macho_relocation_info
<P
>*)((char*)fHeader 
+ fDynamicSymbolTable
->extreloff()); 
 497                                                 if ( fDynamicSymbolTable
->extreloff() < linkEditSegment
->fileoff() ) 
 498                                                         throw "external relocations not in __LINKEDIT"; 
 499                                                 if ( (fDynamicSymbolTable
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 500                                                         throw "external relocations not in __LINKEDIT"; 
 501                                                 if ( (fDynamicSymbolTable
->extreloff() % sizeof(pint_t
)) != 0 ) 
 502                                                         throw "external relocations table not pointer aligned"; 
 506                         case LC_SEGMENT_SPLIT_INFO
: 
 508                                         if ( isStaticExecutable 
) 
 509                                                 throw "LC_SEGMENT_SPLIT_INFO should not be used in static executable"; 
 510                                         const macho_linkedit_data_command
<P
>* info 
= (struct macho_linkedit_data_command
<P
>*)cmd
; 
 511                                         if ( info
->dataoff() < linkEditSegment
->fileoff() ) 
 512                                                 throw "split seg info not in __LINKEDIT"; 
 513                                         if ( (info
->dataoff()+info
->datasize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 514                                                 throw "split seg info not in __LINKEDIT"; 
 515                                         if ( (info
->dataoff() % sizeof(pint_t
)) != 0 ) 
 516                                                 throw "split seg info table not pointer aligned"; 
 517                                         if ( (info
->datasize() % sizeof(pint_t
)) != 0 ) 
 518                                                 throw "split seg info size not a multiple of pointer size"; 
 522                 cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 524         if ( !isStaticExecutable 
&& !foundDynamicSymTab 
) 
 525                 throw "missing dynamic symbol table"; 
 526         if ( fStrings 
== NULL 
) 
 527                 throw "missing symbol table"; 
 531 template <typename A
> 
 532 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
) 
 534         uint8_t sectionType 
= (sect
->flags() & SECTION_TYPE
); 
 535         if ( sectionType 
== S_ZEROFILL 
) { 
 536                 if ( sect
->offset() != 0 ) 
 537                         throwf("section offset should be zero for zero-fill section %s", sect
->sectname()); 
 540         // more section tests here 
 543 template <typename A
> 
 544 void MachOChecker
<A
>::checkIndirectSymbolTable() 
 546         const macho_load_command
<P
>* const cmds 
= (macho_load_command
<P
>*)((uint8_t*)fHeader 
+ sizeof(macho_header
<P
>)); 
 547         const uint32_t cmd_count 
= fHeader
->ncmds(); 
 548         const macho_load_command
<P
>* cmd 
= cmds
; 
 549         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 550                 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD 
) { 
 551                         const macho_segment_command
<P
>* segCmd 
= (const macho_segment_command
<P
>*)cmd
; 
 552                         const macho_section
<P
>* const sectionsStart 
= (macho_section
<P
>*)((char*)segCmd 
+ sizeof(macho_segment_command
<P
>)); 
 553                         const macho_section
<P
>* const sectionsEnd 
= §ionsStart
[segCmd
->nsects()]; 
 554                         for(const macho_section
<P
>* sect 
= sectionsStart
; sect 
< sectionsEnd
; ++sect
) { 
 555                                 // make sure all magic sections that use indirect symbol table fit within it 
 557                                 uint32_t elementSize 
= 0; 
 558                                 switch ( sect
->flags() & SECTION_TYPE 
) { 
 560                                                 elementSize 
= sect
->reserved2(); 
 561                                                 start 
= sect
->reserved1(); 
 563                                         case S_LAZY_SYMBOL_POINTERS
: 
 564                                         case S_NON_LAZY_SYMBOL_POINTERS
: 
 565                                                 elementSize 
= sizeof(pint_t
); 
 566                                                 start 
= sect
->reserved1(); 
 569                                 if ( elementSize 
!= 0 ) { 
 570                                         uint32_t count 
= sect
->size() / elementSize
; 
 571                                         if ( (count
*elementSize
) != sect
->size() ) 
 572                                                 throwf("%s section size is not an even multiple of element size", sect
->sectname()); 
 573                                         if ( (start
+count
) > fIndirectTableCount 
) 
 574                                                 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount 
); 
 578                 cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 583 template <typename A
> 
 584 void MachOChecker
<A
>::checkSymbolTable() 
 586         // verify no duplicate external symbol names 
 587         if ( fDynamicSymbolTable 
!= NULL 
) { 
 588                 StringSet externalNames
; 
 589                 const macho_nlist
<P
>* const     exportedStart 
= &fSymbols
[fDynamicSymbolTable
->iextdefsym()]; 
 590                 const macho_nlist
<P
>* const exportedEnd 
= &exportedStart
[fDynamicSymbolTable
->nextdefsym()]; 
 591                 int i 
= fDynamicSymbolTable
->iextdefsym(); 
 592                 for(const macho_nlist
<P
>* p 
= exportedStart
; p 
< exportedEnd
; ++p
, ++i
) { 
 593                         const char* symName 
= &fStrings
[p
->n_strx()]; 
 594                         if ( symName 
> fStringsEnd 
) 
 595                                 throw "string index out of range"; 
 596                         //fprintf(stderr, "sym[%d] = %s\n", i, symName); 
 597                         if ( externalNames
.find(symName
) != externalNames
.end() ) 
 598                                 throwf("duplicate external symbol: %s", symName
); 
 599                         externalNames
.insert(symName
); 
 601                 // verify no undefines with same name as an external symbol 
 602                 const macho_nlist
<P
>* const     undefinesStart 
= &fSymbols
[fDynamicSymbolTable
->iundefsym()]; 
 603                 const macho_nlist
<P
>* const undefinesEnd 
= &undefinesStart
[fDynamicSymbolTable
->nundefsym()]; 
 604                 for(const macho_nlist
<P
>* p 
= undefinesStart
; p 
< undefinesEnd
; ++p
) { 
 605                         const char* symName 
= &fStrings
[p
->n_strx()]; 
 606                         if ( symName 
> fStringsEnd 
) 
 607                                 throw "string index out of range"; 
 608                         if ( externalNames
.find(symName
) != externalNames
.end() ) 
 609                                 throwf("undefine with same name as external symbol: %s", symName
); 
 611                 // verify all N_SECT values are valid 
 612                 for(const macho_nlist
<P
>* p 
= fSymbols
; p 
< &fSymbols
[fSymbolCount
]; ++p
) { 
 613                         uint8_t type 
= p
->n_type(); 
 614                         if ( ((type 
& N_STAB
) == 0) && ((type 
& N_TYPE
) == N_SECT
) ) { 
 615                                 if ( p
->n_sect() > fSectionCount 
) { 
 616                                         throwf("symbol '%s' has n_sect=%d which is too large", &fStrings
[p
->n_strx()], p
->n_sect()); 
 625 ppc::P::uint_t MachOChecker
<ppc
>::relocBase() 
 627         if ( fHeader
->flags() & MH_SPLIT_SEGS 
) 
 628                 return fFirstWritableSegment
->vmaddr(); 
 630                 return fFirstSegment
->vmaddr(); 
 634 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase() 
 636         if ( fWriteableSegmentWithAddrOver4G 
)  
 637                 return fFirstWritableSegment
->vmaddr(); 
 639                 return fFirstSegment
->vmaddr(); 
 643 x86::P::uint_t MachOChecker
<x86
>::relocBase() 
 645         if ( fHeader
->flags() & MH_SPLIT_SEGS 
) 
 646                 return fFirstWritableSegment
->vmaddr(); 
 648                 return fFirstSegment
->vmaddr(); 
 652 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase() 
 654         // check for split-seg 
 655         return fFirstWritableSegment
->vmaddr(); 
 659 arm::P::uint_t MachOChecker
<arm
>::relocBase() 
 661         if ( fHeader
->flags() & MH_SPLIT_SEGS 
) 
 662                 return fFirstWritableSegment
->vmaddr(); 
 664                 return fFirstSegment
->vmaddr(); 
 668 template <typename A
> 
 669 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
) 
 671         const macho_load_command
<P
>* const cmds 
= (macho_load_command
<P
>*)((uint8_t*)fHeader 
+ sizeof(macho_header
<P
>)); 
 672         const uint32_t cmd_count 
= fHeader
->ncmds(); 
 673         const macho_load_command
<P
>* cmd 
= cmds
; 
 674         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 675                 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD 
) { 
 676                         const macho_segment_command
<P
>* segCmd 
= (const macho_segment_command
<P
>*)cmd
; 
 677                         if ( (address 
>= segCmd
->vmaddr()) && (address 
< segCmd
->vmaddr()+segCmd
->vmsize()) ) { 
 678                                 // if segment is writable, we are fine 
 679                                 if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 )  
 681                                 // could be a text reloc, make sure section bit is set 
 682                                 const macho_section
<P
>* const sectionsStart 
= (macho_section
<P
>*)((char*)segCmd 
+ sizeof(macho_segment_command
<P
>)); 
 683                                 const macho_section
<P
>* const sectionsEnd 
= §ionsStart
[segCmd
->nsects()]; 
 684                                 for(const macho_section
<P
>* sect 
= sectionsStart
; sect 
< sectionsEnd
; ++sect
) { 
 685                                         if ( (sect
->addr() <= address
) && (address 
< (sect
->addr()+sect
->size())) ) { 
 686                                                 // found section for this address, if has relocs we are fine 
 687                                                 return ( (sect
->flags() & (S_ATTR_EXT_RELOC
|S_ATTR_LOC_RELOC
)) != 0 ); 
 692                 cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 699 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 701         if ( reloc
->r_length() != 2 )  
 702                 throw "bad external relocation length"; 
 703         if ( reloc
->r_type() != GENERIC_RELOC_VANILLA 
)  
 704                 throw "unknown external relocation type"; 
 705         if ( reloc
->r_pcrel() != 0 )  
 706                 throw "bad external relocation pc_rel"; 
 707         if ( reloc
->r_extern() == 0 ) 
 708                 throw "local relocation found with external relocations"; 
 709         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 710                 throw "external relocation address not in writable segment"; 
 711         // FIX: check r_symbol 
 715 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 717         if ( reloc
->r_length() != 3 )  
 718                 throw "bad external relocation length"; 
 719         if ( reloc
->r_type() != GENERIC_RELOC_VANILLA 
)  
 720                 throw "unknown external relocation type"; 
 721         if ( reloc
->r_pcrel() != 0 )  
 722                 throw "bad external relocation pc_rel"; 
 723         if ( reloc
->r_extern() == 0 ) 
 724                 throw "local relocation found with external relocations"; 
 725         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 726                 throw "external relocation address not in writable segment"; 
 727         // FIX: check r_symbol 
 731 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 733         if ( reloc
->r_length() != 2 )  
 734                 throw "bad external relocation length"; 
 735         if ( reloc
->r_type() != GENERIC_RELOC_VANILLA 
)  
 736                 throw "unknown external relocation type"; 
 737         if ( reloc
->r_pcrel() != 0 )  
 738                 throw "bad external relocation pc_rel"; 
 739         if ( reloc
->r_extern() == 0 ) 
 740                 throw "local relocation found with external relocations"; 
 741         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 742                 throw "external relocation address not in writable segment"; 
 743         // FIX: check r_symbol 
 748 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 750         if ( reloc
->r_length() != 3 )  
 751                 throw "bad external relocation length"; 
 752         if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED 
)  
 753                 throw "unknown external relocation type"; 
 754         if ( reloc
->r_pcrel() != 0 )  
 755                 throw "bad external relocation pc_rel"; 
 756         if ( reloc
->r_extern() == 0 )  
 757                 throw "local relocation found with external relocations"; 
 758         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 759                 throw "exernal relocation address not in writable segment"; 
 760         // FIX: check r_symbol 
 764 void MachOChecker
<arm
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 766         if ( reloc
->r_length() != 2 )  
 767                 throw "bad external relocation length"; 
 768         if ( reloc
->r_type() != ARM_RELOC_VANILLA 
)  
 769                 throw "unknown external relocation type"; 
 770         if ( reloc
->r_pcrel() != 0 )  
 771                 throw "bad external relocation pc_rel"; 
 772         if ( reloc
->r_extern() == 0 ) 
 773                 throw "local relocation found with external relocations"; 
 774         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 775                 throw "external relocation address not in writable segment"; 
 776         // FIX: check r_symbol 
 781 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 783         if ( reloc
->r_address() & R_SCATTERED 
) { 
 785                 const macho_scattered_relocation_info
<P
>* sreloc 
= (const macho_scattered_relocation_info
<P
>*)reloc
; 
 790                 // ignore pair relocs 
 791                 if ( reloc
->r_type() == PPC_RELOC_PAIR 
)  
 794                 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 795                         throwf("local relocation address 0x%08X not in writable segment", reloc
->r_address()); 
 801 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 803         if ( reloc
->r_length() != 3 )  
 804                 throw "bad local relocation length"; 
 805         if ( reloc
->r_type() != GENERIC_RELOC_VANILLA 
)  
 806                 throw "unknown local relocation type"; 
 807         if ( reloc
->r_pcrel() != 0 )  
 808                 throw "bad local relocation pc_rel"; 
 809         if ( reloc
->r_extern() != 0 )  
 810                 throw "external relocation found with local relocations"; 
 811         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 812                 throw "local relocation address not in writable segment"; 
 816 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 822 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 824         if ( reloc
->r_length() != 3 )  
 825                 throw "bad local relocation length"; 
 826         if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED 
)  
 827                 throw "unknown local relocation type"; 
 828         if ( reloc
->r_pcrel() != 0 )  
 829                 throw "bad local relocation pc_rel"; 
 830         if ( reloc
->r_extern() != 0 )  
 831                 throw "external relocation found with local relocations"; 
 832         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 833                 throw "local relocation address not in writable segment"; 
 837 void MachOChecker
<arm
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 839         if ( reloc
->r_address() & R_SCATTERED 
) { 
 841                 const macho_scattered_relocation_info
<P
>* sreloc 
= (const macho_scattered_relocation_info
<P
>*)reloc
; 
 842                 if ( sreloc
->r_length() != 2 )  
 843                         throw "bad local scattered relocation length"; 
 844                 if ( sreloc
->r_type() != ARM_RELOC_PB_LA_PTR 
)  
 845                         throw "bad local scattered relocation type"; 
 848                 if ( reloc
->r_length() != 2 )  
 849                         throw "bad local relocation length"; 
 850                 if ( reloc
->r_extern() != 0 )  
 851                         throw "external relocation found with local relocations"; 
 852                 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 853                         throw "local relocation address not in writable segment"; 
 857 template <typename A
> 
 858 void MachOChecker
<A
>::checkRelocations() 
 860         // external relocations should be sorted to minimize dyld symbol lookups 
 861         // therefore every reloc with the same r_symbolnum value should be contiguous  
 862         std::set
<uint32_t> previouslySeenSymbolIndexes
; 
 863         uint32_t lastSymbolIndex 
= 0xFFFFFFFF; 
 864         const macho_relocation_info
<P
>* const externRelocsEnd 
= &fExternalRelocations
[fExternalRelocationsCount
]; 
 865         for (const macho_relocation_info
<P
>* reloc 
= fExternalRelocations
; reloc 
< externRelocsEnd
; ++reloc
) { 
 866                 this->checkExternalReloation(reloc
); 
 867                 if ( reloc
->r_symbolnum() != lastSymbolIndex 
) { 
 868                         if ( previouslySeenSymbolIndexes
.count(reloc
->r_symbolnum()) != 0 ) 
 869                                 throw "external relocations not sorted"; 
 870                         previouslySeenSymbolIndexes
.insert(lastSymbolIndex
); 
 871                         lastSymbolIndex 
= reloc
->r_symbolnum(); 
 875         const macho_relocation_info
<P
>* const localRelocsEnd 
= &fLocalRelocations
[fLocalRelocationsCount
]; 
 876         for (const macho_relocation_info
<P
>* reloc 
= fLocalRelocations
; reloc 
< localRelocsEnd
; ++reloc
) { 
 877                 this->checkLocalReloation(reloc
); 
 882 static void check(const char* path
) 
 884         struct stat stat_buf
; 
 887                 int fd 
= ::open(path
, O_RDONLY
, 0); 
 889                         throw "cannot open file"; 
 890                 if ( ::fstat(fd
, &stat_buf
) != 0 )  
 891                         throwf("fstat(%s) failed, errno=%d\n", path
, errno
); 
 892                 uint32_t length 
= stat_buf
.st_size
; 
 893                 uint8_t* p 
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE 
| MAP_PRIVATE
, fd
, 0); 
 894                 if ( p 
== ((uint8_t*)(-1)) ) 
 895                         throw "cannot map file"; 
 897                 const mach_header
* mh 
= (mach_header
*)p
; 
 898                 if ( mh
->magic 
== OSSwapBigToHostInt32(FAT_MAGIC
) ) { 
 899                         const struct fat_header
* fh 
= (struct fat_header
*)p
; 
 900                         const struct fat_arch
* archs 
= (struct fat_arch
*)(p 
+ sizeof(struct fat_header
)); 
 901                         for (unsigned long i
=0; i 
< OSSwapBigToHostInt32(fh
->nfat_arch
); ++i
) { 
 902                                 size_t offset 
= OSSwapBigToHostInt32(archs
[i
].offset
); 
 903                                 size_t size 
= OSSwapBigToHostInt32(archs
[i
].size
); 
 904                                 unsigned int cputype 
= OSSwapBigToHostInt32(archs
[i
].cputype
); 
 907                                 case CPU_TYPE_POWERPC
: 
 908                                         if ( MachOChecker
<ppc
>::validFile(p 
+ offset
) ) 
 909                                                 MachOChecker
<ppc
>::make(p 
+ offset
, size
, path
); 
 911                                                 throw "in universal file, ppc slice does not contain ppc mach-o"; 
 914                                         if ( MachOChecker
<x86
>::validFile(p 
+ offset
) ) 
 915                                                 MachOChecker
<x86
>::make(p 
+ offset
, size
, path
); 
 917                                                 throw "in universal file, i386 slice does not contain i386 mach-o"; 
 919                                 case CPU_TYPE_POWERPC64
: 
 920                                         if ( MachOChecker
<ppc64
>::validFile(p 
+ offset
) ) 
 921                                                 MachOChecker
<ppc64
>::make(p 
+ offset
, size
, path
); 
 923                                                 throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; 
 925                                 case CPU_TYPE_X86_64
: 
 926                                         if ( MachOChecker
<x86_64
>::validFile(p 
+ offset
) ) 
 927                                                 MachOChecker
<x86_64
>::make(p 
+ offset
, size
, path
); 
 929                                                 throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; 
 932                                         if ( MachOChecker
<arm
>::validFile(p 
+ offset
) ) 
 933                                                 MachOChecker
<arm
>::make(p 
+ offset
, size
, path
); 
 935                                                 throw "in universal file, arm slice does not contain arm mach-o"; 
 938                                                 throwf("in universal file, unknown architecture slice 0x%x\n", cputype
); 
 942                 else if ( MachOChecker
<x86
>::validFile(p
) ) { 
 943                         MachOChecker
<x86
>::make(p
, length
, path
); 
 945                 else if ( MachOChecker
<ppc
>::validFile(p
) ) { 
 946                         MachOChecker
<ppc
>::make(p
, length
, path
); 
 948                 else if ( MachOChecker
<ppc64
>::validFile(p
) ) { 
 949                         MachOChecker
<ppc64
>::make(p
, length
, path
); 
 951                 else if ( MachOChecker
<x86_64
>::validFile(p
) ) { 
 952                         MachOChecker
<x86_64
>::make(p
, length
, path
); 
 954                 else if ( MachOChecker
<arm
>::validFile(p
) ) { 
 955                         MachOChecker
<arm
>::make(p
, length
, path
); 
 958                         throw "not a known file type"; 
 961         catch (const char* msg
) { 
 962                 throwf("%s in %s", msg
, path
); 
 967 int main(int argc
, const char* argv
[]) 
 970                 for(int i
=1; i 
< argc
; ++i
) { 
 971                         const char* arg 
= argv
[i
]; 
 972                         if ( arg
[0] == '-' ) { 
 973                                 if ( strcmp(arg
, "-no_content") == 0 ) { 
 977                                         throwf("unknown option: %s\n", arg
); 
 985         catch (const char* msg
) { 
 986                 fprintf(stderr
, "machocheck failed: %s\n", msg
);