1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-  
   3  * Copyright (c) 2006 Apple Computer, 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> 
  32 #include <mach-o/loader.h> 
  33 #include <mach-o/fat.h> 
  34 #include <mach-o/stab.h> 
  35 #include <mach-o/reloc.h> 
  36 #include <mach-o/ppc/reloc.h> 
  37 #include <mach-o/x86_64/reloc.h> 
  41 #include "MachOFileAbstraction.hpp" 
  42 #include "Architectures.hpp" 
  45  __attribute__((noreturn
)) 
  46 void throwf(const char* format
, ...)  
  50         va_start(list
, format
); 
  51         vasprintf(&p
, format
, list
); 
  63         static bool                                                                     validFile(const uint8_t* fileContent
); 
  64         static MachOChecker
<A
>*                                         make(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
)  
  65                                                                                                                 { return new MachOChecker
<A
>(fileContent
, fileLength
, path
); } 
  66         virtual                                                                         ~MachOChecker() {} 
  70         typedef typename 
A::P                                   P
; 
  71         typedef typename 
A::P::E                                E
; 
  72         typedef typename 
A::P::uint_t                   pint_t
; 
  74                                                                                                 MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
); 
  75         void                                                                            checkMachHeader(); 
  76         void                                                                            checkLoadCommands(); 
  77         void                                                                            checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
); 
  78         uint8_t                                                                         loadCommandSizeMask(); 
  79         void                                                                            checkIndirectSymbolTable(); 
  80         void                                                                            checkRelocations(); 
  81         void                                                                            checkExternalReloation(const macho_relocation_info
<P
>* reloc
); 
  82         void                                                                            checkLocalReloation(const macho_relocation_info
<P
>* reloc
); 
  84         bool                                                                            addressInWritableSegment(pint_t address
); 
  87         const macho_header
<P
>*                                          fHeader
; 
  90         const char*                                                                     fStringsEnd
; 
  91         const macho_nlist
<P
>*                                           fSymbols
; 
  92         uint32_t                                                                        fSymbolCount
; 
  93         const uint32_t*                                                         fIndirectTable
; 
  94         uint32_t                                                                        fIndirectTableCount
; 
  95         const macho_relocation_info
<P
>*                         fLocalRelocations
; 
  96         uint32_t                                                                        fLocalRelocationsCount
; 
  97         const macho_relocation_info
<P
>*                         fExternalRelocations
; 
  98         uint32_t                                                                        fExternalRelocationsCount
; 
 100         bool                                                                            fWriteableSegmentWithAddrOver4G
; 
 101         const macho_segment_command
<P
>*                         fFirstSegment
; 
 102         const macho_segment_command
<P
>*                         fFirstWritableSegment
; 
 108 bool MachOChecker
<ppc
>::validFile(const uint8_t* fileContent
) 
 110         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 111         if ( header
->magic() != MH_MAGIC 
) 
 113         if ( header
->cputype() != CPU_TYPE_POWERPC 
) 
 115         switch (header
->filetype()) { 
 126 bool MachOChecker
<ppc64
>::validFile(const uint8_t* fileContent
) 
 128         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 129         if ( header
->magic() != MH_MAGIC_64 
) 
 131         if ( header
->cputype() != CPU_TYPE_POWERPC64 
) 
 133         switch (header
->filetype()) { 
 144 bool MachOChecker
<x86
>::validFile(const uint8_t* fileContent
) 
 146         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 147         if ( header
->magic() != MH_MAGIC 
) 
 149         if ( header
->cputype() != CPU_TYPE_I386 
) 
 151         switch (header
->filetype()) { 
 162 bool MachOChecker
<x86_64
>::validFile(const uint8_t* fileContent
) 
 164         const macho_header
<P
>* header 
= (const macho_header
<P
>*)fileContent
; 
 165         if ( header
->magic() != MH_MAGIC_64 
) 
 167         if ( header
->cputype() != CPU_TYPE_X86_64 
) 
 169         switch (header
->filetype()) { 
 180 template <> uint8_t MachOChecker
<ppc
>::loadCommandSizeMask()    { return 0x03; } 
 181 template <> uint8_t MachOChecker
<ppc64
>::loadCommandSizeMask()  { return 0x07; } 
 182 template <> uint8_t MachOChecker
<x86
>::loadCommandSizeMask()    { return 0x03; } 
 183 template <> uint8_t MachOChecker
<x86_64
>::loadCommandSizeMask() { return 0x07; } 
 186 template <typename A
> 
 187 MachOChecker
<A
>::MachOChecker(const uint8_t* fileContent
, uint32_t fileLength
, const char* path
) 
 188  : fHeader(NULL
), fLength(fileLength
), fStrings(NULL
), fSymbols(NULL
), fSymbolCount(0), fIndirectTableCount(0), 
 189  fLocalRelocations(NULL
),  fLocalRelocationsCount(0),  fExternalRelocations(NULL
),  fExternalRelocationsCount(0), 
 190  fRelocBase(0), fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL
), fFirstWritableSegment(NULL
) 
 193         if ( ! validFile(fileContent
) ) 
 194                 throw "not a mach-o file that can be checked"; 
 196         fPath 
= strdup(path
); 
 197         fHeader 
= (const macho_header
<P
>*)fileContent
; 
 199         // sanity check header 
 202         // check load commands 
 205         checkIndirectSymbolTable(); 
 211 template <typename A
> 
 212 void MachOChecker
<A
>::checkMachHeader() 
 214         if ( (fHeader
->sizeofcmds() + sizeof(macho_header
<P
>)) > fLength 
) 
 215                 throw "sizeofcmds in mach_header is larger than file"; 
 217         uint32_t flags 
= fHeader
->flags(); 
 218         uint32_t invalidBits 
= MH_INCRLINK 
| MH_LAZY_INIT 
| 0xFFFC0000; 
 219         if ( flags 
& invalidBits 
) 
 220                 throw "invalid bits in mach_header flags"; 
 224 template <typename A
> 
 225 void MachOChecker
<A
>::checkLoadCommands() 
 227         // check that all load commands fit within the load command space file 
 228         const uint8_t* const endOfFile 
= (uint8_t*)fHeader 
+ fLength
; 
 229         const uint8_t* const endOfLoadCommands 
= (uint8_t*)fHeader 
+ sizeof(macho_header
<P
>) + fHeader
->sizeofcmds(); 
 230         const uint32_t cmd_count 
= fHeader
->ncmds(); 
 231         const macho_load_command
<P
>* const cmds 
= (macho_load_command
<P
>*)((uint8_t*)fHeader 
+ sizeof(macho_header
<P
>)); 
 232         const macho_load_command
<P
>* cmd 
= cmds
; 
 233         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 234                 uint32_t size 
= cmd
->cmdsize(); 
 235                 if ( (size 
& this->loadCommandSizeMask()) != 0 ) 
 236                         throwf("load command #%d has a unaligned size", i
); 
 237                 const uint8_t* endOfCmd 
= ((uint8_t*)cmd
)+cmd
->cmdsize(); 
 238                 if ( endOfCmd 
> endOfLoadCommands 
) 
 239                         throwf("load command #%d extends beyond the end of the load commands", i
); 
 240                 if ( endOfCmd 
> endOfFile 
) 
 241                         throwf("load command #%d extends beyond the end of the file", i
); 
 242                 switch ( cmd
->cmd()     ) { 
 243                         case macho_segment_command
<P
>::CMD
: 
 249                         case LC_LOAD_DYLINKER
: 
 251                         case macho_routines_command
<P
>::CMD
: 
 252                         case LC_SUB_FRAMEWORK
: 
 253                         case LC_SUB_UMBRELLA
: 
 255                         case LC_TWOLEVEL_HINTS
: 
 256                         case LC_PREBIND_CKSUM
: 
 257                         case LC_LOAD_WEAK_DYLIB
: 
 261                                 throwf("load command #%d is an unknown kind 0x%X", i
, cmd
->cmd()); 
 263                 cmd 
= (const macho_load_command
<P
>*)endOfCmd
; 
 268         std::vector
<std::pair
<pint_t
, pint_t
> > segmentAddressRanges
; 
 269         std::vector
<std::pair
<pint_t
, pint_t
> > segmentFileOffsetRanges
; 
 270         const macho_segment_command
<P
>* linkEditSegment 
= NULL
; 
 271         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 272                 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD 
) { 
 273                         const macho_segment_command
<P
>* segCmd 
= (const macho_segment_command
<P
>*)cmd
; 
 274                         if ( segCmd
->cmdsize() != (sizeof(macho_segment_command
<P
>) + segCmd
->nsects() * sizeof(macho_section_content
<P
>)) ) 
 275                                 throw "invalid segment load command size"; 
 277                         // see if this overlaps another segment address range 
 278                         uint64_t startAddr 
= segCmd
->vmaddr(); 
 279                         uint64_t endAddr 
= startAddr 
+ segCmd
->vmsize(); 
 280                         for (typename 
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it 
= segmentAddressRanges
.begin(); it 
!= segmentAddressRanges
.end(); ++it
) { 
 281                                 if ( it
->first 
< startAddr 
) { 
 282                                         if ( it
->second 
> startAddr 
) 
 283                                                 throw "overlapping segment vm addresses"; 
 285                                 else if ( it
->first 
> startAddr 
) { 
 286                                         if ( it
->first 
< endAddr 
) 
 287                                                 throw "overlapping segment vm addresses"; 
 290                                         throw "overlapping segment vm addresses"; 
 292                                 segmentAddressRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startAddr
, endAddr
)); 
 294                         // see if this overlaps another segment file offset range 
 295                         uint64_t startOffset 
= segCmd
->fileoff(); 
 296                         uint64_t endOffset 
= startOffset 
+ segCmd
->filesize(); 
 297                         for (typename 
std::vector
<std::pair
<pint_t
, pint_t
> >::iterator it 
= segmentFileOffsetRanges
.begin(); it 
!= segmentFileOffsetRanges
.end(); ++it
) { 
 298                                 if ( it
->first 
< startOffset 
) { 
 299                                         if ( it
->second 
> startOffset 
) 
 300                                                 throw "overlapping segment file data"; 
 302                                 else if ( it
->first 
> startOffset 
) { 
 303                                         if ( it
->first 
< endOffset 
) 
 304                                                 throw "overlapping segment file data"; 
 307                                         throw "overlapping segment file data"; 
 309                                 segmentFileOffsetRanges
.push_back(std::make_pair
<pint_t
, pint_t
>(startOffset
, endOffset
)); 
 310                                 // check is within file bounds 
 311                                 if ( (startOffset 
> fLength
) || (endOffset 
> fLength
) ) 
 312                                         throw "segment file data is past end of file"; 
 314                         // verify it fits in file 
 315                         if ( startOffset 
> fLength 
) 
 316                                 throw "segment fileoff does not fit in file"; 
 317                         if ( endOffset 
> fLength 
) 
 318                                 throw "segment fileoff+filesize does not fit in file"; 
 320                         // keep LINKEDIT segment  
 321                         if ( strcmp(segCmd
->segname(), "__LINKEDIT") == 0 ) 
 322                                 linkEditSegment 
= segCmd
; 
 324                         // cache interesting segments 
 325                         if ( fFirstSegment 
== NULL 
) 
 326                                 fFirstSegment 
= segCmd
; 
 327                         if ( (fFirstWritableSegment 
== NULL
) && ((segCmd
->initprot() & VM_PROT_WRITE
) != 0) ) 
 328                                 fFirstWritableSegment 
= segCmd
; 
 330                         // check section ranges 
 331                         const macho_section
<P
>* const sectionsStart 
= (macho_section
<P
>*)((char*)segCmd 
+ sizeof(macho_segment_command
<P
>)); 
 332                         const macho_section
<P
>* const sectionsEnd 
= §ionsStart
[segCmd
->nsects()]; 
 333                         for(const macho_section
<P
>* sect 
= sectionsStart
; sect 
< sectionsEnd
; ++sect
) { 
 334                                 // check all sections are within segment 
 335                                 if ( sect
->addr() < startAddr 
) 
 336                                         throwf("section %s vm address not within segment", sect
->sectname()); 
 337                                 if ( (sect
->addr()+sect
->size()) > endAddr 
) 
 338                                         throwf("section %s vm address not within segment", sect
->sectname()); 
 339                                 if ( (sect
->flags() &SECTION_TYPE
) != S_ZEROFILL 
) { 
 340                                         if ( sect
->offset() < startOffset 
) 
 341                                                 throwf("section %s file offset not within segment", sect
->sectname()); 
 342                                         if ( (sect
->offset()+sect
->size()) > endOffset 
) 
 343                                                 throwf("section %s file offset not within segment", sect
->sectname()); 
 345                                 checkSection(segCmd
, sect
); 
 348                 cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 351         // verify there was a LINKEDIT segment 
 352         if ( linkEditSegment 
== NULL 
) 
 353                 throw "no __LINKEDIT segment"; 
 355         // checks for executables 
 356         bool isStaticExecutable 
= false; 
 357         if ( fHeader
->filetype() == MH_EXECUTE 
) { 
 358                 isStaticExecutable 
= true; 
 360                 for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 361                         switch ( cmd
->cmd() ) { 
 362                                 case LC_LOAD_DYLINKER
: 
 363                                         // the existence of a dyld load command makes a executable dynamic 
 364                                         isStaticExecutable 
= false; 
 367                         cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 369                 if ( isStaticExecutable 
) { 
 370                         if ( fHeader
->flags() != MH_NOUNDEFS 
) 
 371                                 throw "invalid bits in mach_header flags for static executable"; 
 375         // check LC_SYMTAB and LC_DYSYMTAB 
 377         bool foundDynamicSymTab 
= false; 
 378         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 379                 switch ( cmd
->cmd() ) { 
 382                                         const macho_symtab_command
<P
>* symtab 
= (macho_symtab_command
<P
>*)cmd
; 
 383                                         fSymbolCount 
= symtab
->nsyms(); 
 384                                         fSymbols 
= (const macho_nlist
<P
>*)((char*)fHeader 
+ symtab
->symoff()); 
 385                                         if ( symtab
->symoff() < linkEditSegment
->fileoff() ) 
 386                                                 throw "symbol table not in __LINKEDIT"; 
 387                                         if ( (symtab
->symoff() + fSymbolCount
*sizeof(macho_nlist
<P
>*)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 388                                                 throw "symbol table end not in __LINKEDIT"; 
 389                                         fStrings 
= (char*)fHeader 
+ symtab
->stroff(); 
 390                                         fStringsEnd 
= fStrings 
+ symtab
->strsize(); 
 391                                         if ( symtab
->stroff() < linkEditSegment
->fileoff() ) 
 392                                                 throw "string pool not in __LINKEDIT"; 
 393                                         if ( (symtab
->stroff()+symtab
->strsize()) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 394                                                 throw "string pool extends beyond __LINKEDIT"; 
 399                                         if ( isStaticExecutable 
) 
 400                                                 throw "LC_DYSYMTAB should not be used in static executable"; 
 401                                         foundDynamicSymTab 
= true; 
 402                                         const macho_dysymtab_command
<P
>* dsymtab 
= (struct macho_dysymtab_command
<P
>*)cmd
; 
 403                                         fIndirectTable 
= (uint32_t*)((char*)fHeader 
+ dsymtab
->indirectsymoff()); 
 404                                         fIndirectTableCount 
= dsymtab
->nindirectsyms(); 
 405                                         if ( dsymtab
->indirectsymoff() < linkEditSegment
->fileoff() ) 
 406                                                 throw "indirect symbol table not in __LINKEDIT"; 
 407                                         if ( (dsymtab
->indirectsymoff()+fIndirectTableCount
*8) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 408                                                 throw "indirect symbol table not in __LINKEDIT"; 
 409                                         fLocalRelocationsCount 
= dsymtab
->nlocrel(); 
 410                                         if ( fLocalRelocationsCount 
!= 0 ) { 
 411                                                 fLocalRelocations 
= (const macho_relocation_info
<P
>*)((char*)fHeader 
+ dsymtab
->locreloff()); 
 412                                                 if ( dsymtab
->locreloff() < linkEditSegment
->fileoff() ) 
 413                                                         throw "local relocations not in __LINKEDIT"; 
 414                                                 if ( (dsymtab
->locreloff()+fLocalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 415                                                         throw "local relocations not in __LINKEDIT"; 
 417                                         fExternalRelocationsCount 
= dsymtab
->nextrel(); 
 418                                         if ( fExternalRelocationsCount 
!= 0 ) { 
 419                                                 fExternalRelocations 
= (const macho_relocation_info
<P
>*)((char*)fHeader 
+ dsymtab
->extreloff()); 
 420                                                 if ( dsymtab
->extreloff() < linkEditSegment
->fileoff() ) 
 421                                                         throw "local relocations not in __LINKEDIT"; 
 422                                                 if ( (dsymtab
->extreloff()+fExternalRelocationsCount
*sizeof(macho_relocation_info
<P
>)) > (linkEditSegment
->fileoff()+linkEditSegment
->filesize()) ) 
 423                                                         throw "local relocations not in __LINKEDIT"; 
 428                 cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 430         if ( !isStaticExecutable 
&& !foundDynamicSymTab 
) 
 431                 throw "missing dynamic symbol table"; 
 432         if ( fStrings 
== NULL 
) 
 433                 throw "missing symbol table"; 
 435         fRelocBase 
= this->relocBase(); 
 439 template <typename A
> 
 440 void MachOChecker
<A
>::checkSection(const macho_segment_command
<P
>* segCmd
, const macho_section
<P
>* sect
) 
 442         uint8_t sectionType 
= (sect
->flags() & SECTION_TYPE
); 
 443         if ( sectionType 
== S_ZEROFILL 
) { 
 444                 if ( sect
->offset() != 0 ) 
 445                         throwf("section offset should be zero for zero-fill section %s", sect
->sectname()); 
 448         // more section tests here 
 451 template <typename A
> 
 452 void MachOChecker
<A
>::checkIndirectSymbolTable() 
 454         const macho_load_command
<P
>* const cmds 
= (macho_load_command
<P
>*)((uint8_t*)fHeader 
+ sizeof(macho_header
<P
>)); 
 455         const uint32_t cmd_count 
= fHeader
->ncmds(); 
 456         const macho_load_command
<P
>* cmd 
= cmds
; 
 457         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 458                 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD 
) { 
 459                         const macho_segment_command
<P
>* segCmd 
= (const macho_segment_command
<P
>*)cmd
; 
 460                         const macho_section
<P
>* const sectionsStart 
= (macho_section
<P
>*)((char*)segCmd 
+ sizeof(macho_segment_command
<P
>)); 
 461                         const macho_section
<P
>* const sectionsEnd 
= §ionsStart
[segCmd
->nsects()]; 
 462                         for(const macho_section
<P
>* sect 
= sectionsStart
; sect 
< sectionsEnd
; ++sect
) { 
 463                                 // make sure all magic sections that use indirect symbol table fit within it 
 465                                 uint32_t elementSize 
= 0; 
 466                                 switch ( sect
->flags() & SECTION_TYPE 
) { 
 468                                                 elementSize 
= sect
->reserved2(); 
 469                                                 start 
= sect
->reserved1(); 
 471                                         case S_LAZY_SYMBOL_POINTERS
: 
 472                                         case S_NON_LAZY_SYMBOL_POINTERS
: 
 473                                                 elementSize 
= sizeof(pint_t
); 
 474                                                 start 
= sect
->reserved1(); 
 477                                 if ( elementSize 
!= 0 ) { 
 478                                         uint32_t count 
= sect
->size() / elementSize
; 
 479                                         if ( (count
*elementSize
) != sect
->size() ) 
 480                                                 throwf("%s section size is not an even multiple of element size", sect
->sectname()); 
 481                                         if ( (start
+count
) > fIndirectTableCount 
) 
 482                                                 throwf("%s section references beyond end of indirect symbol table (%d > %d)", sect
->sectname(), start
+count
, fIndirectTableCount 
); 
 486                 cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 493 ppc::P::uint_t MachOChecker
<ppc
>::relocBase() 
 495         if ( fHeader
->flags() & MH_SPLIT_SEGS 
) 
 496                 return fFirstWritableSegment
->vmaddr(); 
 498                 return fFirstSegment
->vmaddr(); 
 502 ppc64::P::uint_t MachOChecker
<ppc64
>::relocBase() 
 504         if ( fWriteableSegmentWithAddrOver4G 
)  
 505                 return fFirstWritableSegment
->vmaddr(); 
 507                 return fFirstSegment
->vmaddr(); 
 511 x86::P::uint_t MachOChecker
<x86
>::relocBase() 
 513         if ( fHeader
->flags() & MH_SPLIT_SEGS 
) 
 514                 return fFirstWritableSegment
->vmaddr(); 
 516                 return fFirstSegment
->vmaddr(); 
 520 x86_64::P::uint_t MachOChecker
<x86_64
>::relocBase() 
 522         // check for split-seg 
 523         return fFirstWritableSegment
->vmaddr(); 
 527 template <typename A
> 
 528 bool MachOChecker
<A
>::addressInWritableSegment(pint_t address
) 
 530         const macho_load_command
<P
>* const cmds 
= (macho_load_command
<P
>*)((uint8_t*)fHeader 
+ sizeof(macho_header
<P
>)); 
 531         const uint32_t cmd_count 
= fHeader
->ncmds(); 
 532         const macho_load_command
<P
>* cmd 
= cmds
; 
 533         for (uint32_t i 
= 0; i 
< cmd_count
; ++i
) { 
 534                 if ( cmd
->cmd() == macho_segment_command
<P
>::CMD 
) { 
 535                         const macho_segment_command
<P
>* segCmd 
= (const macho_segment_command
<P
>*)cmd
; 
 536                         if ( (segCmd
->initprot() & VM_PROT_WRITE
) != 0 ) { 
 537                                 if ( (address 
>= segCmd
->vmaddr()) && (address 
< segCmd
->vmaddr()+segCmd
->vmsize()) ) 
 541                 cmd 
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize()); 
 548 void MachOChecker
<ppc
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 554 void MachOChecker
<ppc64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 556         if ( reloc
->r_length() != 3 )  
 557                 throw "bad external relocation length"; 
 558         if ( reloc
->r_type() != GENERIC_RELOC_VANILLA 
)  
 559                 throw "unknown external relocation type"; 
 560         if ( reloc
->r_pcrel() != 0 )  
 561                 throw "bad external relocation pc_rel"; 
 562         if ( reloc
->r_extern() == 0 ) 
 563                 throw "local relocation found with external relocations"; 
 564         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 565                 throw "local relocation address not in writable segment"; 
 566         // FIX: check r_symbol 
 570 void MachOChecker
<x86
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 577 void MachOChecker
<x86_64
>::checkExternalReloation(const macho_relocation_info
<P
>* reloc
) 
 579         if ( reloc
->r_length() != 3 )  
 580                 throw "bad external relocation length"; 
 581         if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED 
)  
 582                 throw "unknown external relocation type"; 
 583         if ( reloc
->r_pcrel() != 0 )  
 584                 throw "bad external relocation pc_rel"; 
 585         if ( reloc
->r_extern() == 0 )  
 586                 throw "local relocation found with external relocations"; 
 587         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 588                 throw "exernal relocation address not in writable segment"; 
 589         // FIX: check r_symbol 
 593 void MachOChecker
<ppc
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 595         if ( reloc
->r_address() & R_SCATTERED 
) { 
 597                 const macho_scattered_relocation_info
<P
>* sreloc 
= (const macho_scattered_relocation_info
<P
>*)reloc
; 
 603                 if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 604                         throw "local relocation address not in writable segment"; 
 610 void MachOChecker
<ppc64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 612         if ( reloc
->r_length() != 3 )  
 613                 throw "bad local relocation length"; 
 614         if ( reloc
->r_type() != GENERIC_RELOC_VANILLA 
)  
 615                 throw "unknown local relocation type"; 
 616         if ( reloc
->r_pcrel() != 0 )  
 617                 throw "bad local relocation pc_rel"; 
 618         if ( reloc
->r_extern() != 0 )  
 619                 throw "external relocation found with local relocations"; 
 620         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 621                 throw "local relocation address not in writable segment"; 
 625 void MachOChecker
<x86
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 631 void MachOChecker
<x86_64
>::checkLocalReloation(const macho_relocation_info
<P
>* reloc
) 
 633         if ( reloc
->r_length() != 3 )  
 634                 throw "bad local relocation length"; 
 635         if ( reloc
->r_type() != X86_64_RELOC_UNSIGNED 
)  
 636                 throw "unknown local relocation type"; 
 637         if ( reloc
->r_pcrel() != 0 )  
 638                 throw "bad local relocation pc_rel"; 
 639         if ( reloc
->r_extern() != 0 )  
 640                 throw "external relocation found with local relocations"; 
 641         if ( ! this->addressInWritableSegment(reloc
->r_address() + this->relocBase()) ) 
 642                 throw "local relocation address not in writable segment"; 
 647 template <typename A
> 
 648 void MachOChecker
<A
>::checkRelocations() 
 650         const macho_relocation_info
<P
>* const externRelocsEnd 
= &fExternalRelocations
[fExternalRelocationsCount
]; 
 651         for (const macho_relocation_info
<P
>* reloc 
= fExternalRelocations
; reloc 
< externRelocsEnd
; ++reloc
) { 
 652                 this->checkExternalReloation(reloc
); 
 655         const macho_relocation_info
<P
>* const localRelocsEnd 
= &fLocalRelocations
[fLocalRelocationsCount
]; 
 656         for (const macho_relocation_info
<P
>* reloc 
= fLocalRelocations
; reloc 
< localRelocsEnd
; ++reloc
) { 
 657                 this->checkLocalReloation(reloc
); 
 662 static void check(const char* path
) 
 664         struct stat stat_buf
; 
 667                 int fd 
= ::open(path
, O_RDONLY
, 0); 
 669                         throw "cannot open file"; 
 670                 ::fstat(fd
, &stat_buf
); 
 671                 uint32_t length 
= stat_buf
.st_size
; 
 672                 uint8_t* p 
= (uint8_t*)::mmap(NULL
, stat_buf
.st_size
, PROT_READ
, MAP_FILE 
| MAP_PRIVATE
, fd
, 0); 
 673                 if ( p 
== ((uint8_t*)(-1)) ) 
 674                         throw "cannot map file"; 
 676                 const mach_header
* mh 
= (mach_header
*)p
; 
 677                 if ( mh
->magic 
== OSSwapBigToHostInt32(FAT_MAGIC
) ) { 
 678                         const struct fat_header
* fh 
= (struct fat_header
*)p
; 
 679                         const struct fat_arch
* archs 
= (struct fat_arch
*)(p 
+ sizeof(struct fat_header
)); 
 680                         for (unsigned long i
=0; i 
< fh
->nfat_arch
; ++i
) { 
 681                                 if ( archs
[i
].cputype 
== CPU_TYPE_POWERPC 
) { 
 682                                         if ( MachOChecker
<ppc
>::validFile(p 
+ archs
[i
].offset
) ) 
 683                                                 MachOChecker
<ppc
>::make(p 
+ archs
[i
].offset
, archs
[i
].size
, path
); 
 685                                                 throw "in universal file, ppc slice does not contain ppc mach-o"; 
 687                                 else if ( archs
[i
].cputype 
== CPU_TYPE_I386 
) { 
 688                                         if ( MachOChecker
<x86
>::validFile(p 
+ archs
[i
].offset
) ) 
 689                                                 MachOChecker
<x86
>::make(p 
+ archs
[i
].offset
, archs
[i
].size
, path
); 
 691                                                 throw "in universal file, i386 slice does not contain i386 mach-o"; 
 693                                 else if ( archs
[i
].cputype 
== CPU_TYPE_POWERPC64 
) { 
 694                                         if ( MachOChecker
<ppc64
>::validFile(p 
+ archs
[i
].offset
) ) 
 695                                                 MachOChecker
<ppc64
>::make(p 
+ archs
[i
].offset
, archs
[i
].size
, path
); 
 697                                                 throw "in universal file, ppc64 slice does not contain ppc64 mach-o"; 
 699                                 else if ( archs
[i
].cputype 
== CPU_TYPE_X86_64 
) { 
 700                                         if ( MachOChecker
<x86_64
>::validFile(p 
+ archs
[i
].offset
) ) 
 701                                                 MachOChecker
<x86_64
>::make(p 
+ archs
[i
].offset
, archs
[i
].size
, path
); 
 703                                                 throw "in universal file, x86_64 slice does not contain x86_64 mach-o"; 
 706                                                 throw "in universal file, unknown architecture slice"; 
 710                 else if ( MachOChecker
<x86
>::validFile(p
) ) { 
 711                         MachOChecker
<x86
>::make(p
, length
, path
); 
 713                 else if ( MachOChecker
<ppc
>::validFile(p
) ) { 
 714                         MachOChecker
<ppc
>::make(p
, length
, path
); 
 716                 else if ( MachOChecker
<ppc64
>::validFile(p
) ) { 
 717                         MachOChecker
<ppc64
>::make(p
, length
, path
); 
 719                 else if ( MachOChecker
<x86_64
>::validFile(p
) ) { 
 720                         MachOChecker
<x86_64
>::make(p
, length
, path
); 
 723                         throw "not a known file type"; 
 726         catch (const char* msg
) { 
 727                 throwf("%s in %s", msg
, path
); 
 732 int main(int argc
, const char* argv
[]) 
 735                 for(int i
=1; i 
< argc
; ++i
) { 
 736                         const char* arg 
= argv
[i
]; 
 737                         if ( arg
[0] == '-' ) { 
 738                                 if ( strcmp(arg
, "-no_content") == 0 ) { 
 742                                         throwf("unknown option: %s\n", arg
); 
 750         catch (const char* msg
) { 
 751                 fprintf(stderr
, "machocheck failed: %s\n", msg
);