2  * Copyright (c) 2017 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@ 
  24 #include <sys/types.h> 
  26 #include <sys/errno.h> 
  27 #include <sys/fcntl.h> 
  29 #include <mach/mach.h> 
  35 #include <mach-o/reloc.h> 
  36 #include <mach-o/x86_64/reloc.h> 
  37 #include <mach-o/nlist.h> 
  38 #include <TargetConditionals.h> 
  40 #include "MachOAnalyzer.h" 
  41 #include "CodeSigningTypes.h" 
  44 // FIXME: We should get this from cctools 
  45 #define DYLD_CACHE_ADJ_V2_FORMAT 0x7F 
  50 const MachOAnalyzer
* MachOAnalyzer::validMainExecutable(Diagnostics
& diag
, const mach_header
* mh
, const char* path
, uint64_t sliceLength
, 
  51                                                         const GradedArchs
& archs
, Platform platform
) 
  53     const MachOAnalyzer
* result 
= (const MachOAnalyzer
*)mh
; 
  54     if ( !result
->validMachOForArchAndPlatform(diag
, (size_t)sliceLength
, path
, archs
, platform
, true) ) 
  56     if ( !result
->isDynamicExecutable() ) 
  62 bool MachOAnalyzer::loadFromBuffer(Diagnostics
& diag
, const closure::FileSystem
& fileSystem
, 
  63                                    const char* path
, const GradedArchs
& archs
, Platform platform
, 
  64                                    closure::LoadedFileInfo
& info
) 
  66     // if fat, remap just slice needed 
  67     bool fatButMissingSlice
; 
  68     const FatFile
*       fh 
= (FatFile
*)info
.fileContent
; 
  69     uint64_t sliceOffset 
= info
.sliceOffset
; 
  70     uint64_t sliceLen 
= info
.sliceLen
; 
  71     if ( fh
->isFatFileWithSlice(diag
, info
.fileContentLen
, archs
, info
.isOSBinary
, sliceOffset
, sliceLen
, fatButMissingSlice
) ) { 
  72         // unmap anything before slice 
  73         fileSystem
.unloadPartialFile(info
, sliceOffset
, sliceLen
); 
  74         // Update the info to keep track of the new slice offset. 
  75         info
.sliceOffset 
= sliceOffset
; 
  76         info
.sliceLen 
= sliceLen
; 
  78     else if ( diag
.hasError() ) { 
  79         // We must have generated an error in the fat file parsing so use that error 
  80         fileSystem
.unloadFile(info
); 
  83     else if ( fatButMissingSlice 
) { 
  84         diag
.error("missing compatible arch in %s", path
); 
  85         fileSystem
.unloadFile(info
); 
  89     const MachOAnalyzer
* mh 
= (MachOAnalyzer
*)info
.fileContent
; 
  91     // validate is mach-o of requested arch and platform 
  92     if ( !mh
->validMachOForArchAndPlatform(diag
, (size_t)info
.sliceLen
, path
, archs
, platform
, info
.isOSBinary
) ) { 
  93         fileSystem
.unloadFile(info
); 
  97     // if has zero-fill expansion, re-map 
  98     mh 
= mh
->remapIfZeroFill(diag
, fileSystem
, info
); 
 100     // on error, remove mappings and return nullptr 
 101     if ( diag
.hasError() ) { 
 102         fileSystem
.unloadFile(info
); 
 106     // now that LINKEDIT is at expected offset, finish validation 
 107     mh
->validLinkedit(diag
, path
); 
 109     // on error, remove mappings and return nullptr 
 110     if ( diag
.hasError() ) { 
 111         fileSystem
.unloadFile(info
); 
 119 closure::LoadedFileInfo 
MachOAnalyzer::load(Diagnostics
& diag
, const closure::FileSystem
& fileSystem
, 
 120                                             const char* path
, const GradedArchs
& archs
, Platform platform
, char realerPath
[MAXPATHLEN
]) 
 122     // FIXME: This should probably be an assert, but if we happen to have a diagnostic here then something is wrong 
 123     // above us and we should quickly return instead of doing unnecessary work. 
 125         return closure::LoadedFileInfo(); 
 127     closure::LoadedFileInfo info
; 
 128     if (!fileSystem
.loadFile(path
, info
, realerPath
, ^(const char *format
, ...) { 
 130         va_start(list
, format
); 
 131         diag
.error(format
, list
); 
 134         return closure::LoadedFileInfo(); 
 137     // If we now have an error, but succeeded, then we must have tried multiple paths, one of which errored, but 
 138     // then succeeded on a later path.  So clear the error. 
 142     bool loaded 
= loadFromBuffer(diag
, fileSystem
, path
, archs
, platform
, info
); 
 148 // for use with already mmap()ed file 
 149 bool MachOAnalyzer::isOSBinary(int fd
, uint64_t sliceOffset
, uint64_t sliceSize
) const 
 157     if ( !this->hasCodeSignature(sigOffset
, sigSize
) ) 
 160     // register code signature 
 161     fsignatures_t sigreg
; 
 162     sigreg
.fs_file_start 
= sliceOffset
;                // start of mach-o slice in fat file 
 163     sigreg
.fs_blob_start 
= (void*)(long)sigOffset
;     // start of CD in mach-o file 
 164     sigreg
.fs_blob_size  
= sigSize
;                    // size of CD 
 165     if ( ::fcntl(fd
, F_ADDFILESIGS_RETURN
, &sigreg
) == -1 ) 
 168     // ask if code signature is for something in the OS 
 169     fgetsigsinfo siginfo 
= { (off_t
)sliceOffset
, GETSIGSINFO_PLATFORM_BINARY
, 0 }; 
 170     if ( ::fcntl(fd
, F_GETSIGSINFO
, &siginfo
) == -1 ) 
 173     return (siginfo
.fg_sig_is_platform
); 
 179 // for use when just the fat_header has been read 
 180 bool MachOAnalyzer::sliceIsOSBinary(int fd
, uint64_t sliceOffset
, uint64_t sliceSize
) 
 185     // need to mmap() slice so we can find the code signature 
 186         void* mappedSlice 
= ::mmap(nullptr, sliceSize
, PROT_READ
, MAP_PRIVATE
, fd
, sliceOffset
); 
 187         if ( mappedSlice 
== MAP_FAILED 
) 
 190     const MachOAnalyzer
* ma 
= (MachOAnalyzer
*)mappedSlice
; 
 191     bool result 
= ma
->isOSBinary(fd
, sliceOffset
, sliceSize
); 
 192     ::munmap(mappedSlice
, sliceSize
); 
 200 // only used in debug builds of cache builder to verify segment moves are valid 
 201 void MachOAnalyzer::validateDyldCacheDylib(Diagnostics
& diag
, const char* path
) const 
 203     validLinkedit(diag
, path
); 
 204     validSegments(diag
, path
, 0xffffffff); 
 208 uint64_t MachOAnalyzer::mappedSize() const 
 212     analyzeSegmentsLayout(vmSpace
, hasZeroFill
); 
 216 bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics
& diag
, size_t sliceLength
, const char* path
, const GradedArchs
& archs
, Platform reqPlatform
, bool isOSBinary
) const 
 218     // must start with mach-o magic value 
 219     if ( (this->magic 
!= MH_MAGIC
) && (this->magic 
!= MH_MAGIC_64
) ) { 
 220         diag
.error("could not use '%s' because it is not a mach-o file: 0x%08X 0x%08X", path
, this->magic
, this->cputype
); 
 224     if ( !archs
.grade(this->cputype
, this->cpusubtype
, isOSBinary
) ) { 
 225         diag
.error("could not use '%s' because it is not a compatible arch", path
); 
 229     // must be a filetype dyld can load 
 230     switch ( this->filetype 
) { 
 235 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC 
 236         // Allow offline tools to analyze binaries dyld doesn't load 
 243             diag
.error("could not use '%s' because it is not a dylib, bundle, or executable, filetype=0x%08X", path
, this->filetype
); 
 247     // validate load commands structure 
 248     if ( !this->validLoadCommands(diag
, path
, sliceLength
) ) { 
 252     // filter out static executables 
 253     if ( (this->filetype 
== MH_EXECUTE
) && !isDynamicExecutable() ) { 
 254 #if !BUILDING_DYLDINFO && !BUILDING_APP_CACHE_UTIL 
 255         // dyldinfo should be able to inspect static executables such as the kernel 
 256         diag
.error("could not use '%s' because it is a static executable", path
); 
 261     // HACK: If we are asking for no platform, then make sure the binary doesn't have one 
 262 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL 
 264         // A statically linked kernel collection should contain a 0 platform 
 265         __block 
bool foundPlatform 
= false; 
 266         __block 
bool foundBadPlatform 
= false; 
 267         forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) { 
 268             foundPlatform 
= true; 
 269             if ( platform 
!= Platform::unknown 
) { 
 270                 foundBadPlatform 
= true; 
 273         if (!foundPlatform
) { 
 274             diag
.error("could not use '%s' because we expected it to have a platform", path
); 
 277         if (foundBadPlatform
) { 
 278             diag
.error("could not use '%s' because is has the wrong platform", path
); 
 281     } else if ( reqPlatform 
== Platform::unknown 
) { 
 282         // Unfortunately the static kernel has a platform, but kext's don't, so we can't 
 283         // verify the platform of the kernel. 
 284         if ( !isStaticExecutable() ) { 
 285             __block 
bool foundPlatform 
= false; 
 286             forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) { 
 287                 foundPlatform 
= true; 
 290                 diag
.error("could not use '%s' because we expected it to have no platform", path
); 
 296     if ( !this->loadableIntoProcess(reqPlatform
, path
) ) { 
 297         diag
.error("could not use '%s' because it was not built for platform %s", path
, MachOFile::platformName(reqPlatform
)); 
 301     // validate dylib loads 
 302     if ( !validEmbeddedPaths(diag
, reqPlatform
, path
) ) 
 306     if ( !validSegments(diag
, path
, sliceLength
) ) 
 310     if ( this->filetype 
== MH_EXECUTE 
) { 
 311         if ( !validMain(diag
, path
) ) 
 315     // further validations done in validLinkedit() 
 320 bool MachOAnalyzer::validLinkedit(Diagnostics
& diag
, const char* path
) const 
 322     // validate LINKEDIT layout 
 323     if ( !validLinkeditLayout(diag
, path
) ) 
 326     if ( hasLoadCommand(LC_DYLD_CHAINED_FIXUPS
) ) { 
 327         if ( !validChainedFixupsInfo(diag
, path
) ) 
 330 #if SUPPORT_ARCH_arm64e 
 331     else if ( (this->cputype 
== CPU_TYPE_ARM64
) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E
) ) { 
 332         if ( !validChainedFixupsInfoOldArm64e(diag
, path
) ) 
 337         // validate rebasing info 
 338         if ( !validRebaseInfo(diag
, path
) ) 
 341        // validate binding info 
 342         if ( !validBindInfo(diag
, path
) ) 
 349 bool MachOAnalyzer::validLoadCommands(Diagnostics
& diag
, const char* path
, size_t fileLen
) const 
 351     // check load command don't exceed file length 
 352     if ( this->sizeofcmds 
+ machHeaderSize() > fileLen 
) { 
 353         diag
.error("in '%s' load commands exceed length of file", path
); 
 357     // walk all load commands and sanity check them 
 358     Diagnostics walkDiag
; 
 359     forEachLoadCommand(walkDiag
, ^(const load_command
* cmd
, bool& stop
) {}); 
 360     if ( walkDiag
.hasError() ) { 
 361 #if BUILDING_CACHE_BUILDER 
 362         diag
.error("in '%s' %s", path
, walkDiag
.errorMessage().c_str()); 
 364         diag
.error("in '%s' %s", path
, walkDiag
.errorMessage()); 
 369     // check load commands fit in TEXT segment 
 370     __block 
bool foundTEXT    
= false; 
 371     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
 372         if ( strcmp(info
.segName
, "__TEXT") == 0 ) { 
 374             if ( this->sizeofcmds 
+ machHeaderSize() > info
.fileSize 
) { 
 375                 diag
.error("in '%s' load commands exceed length of __TEXT segment", path
); 
 377             if ( info
.fileOffset 
!= 0 ) { 
 378                 diag
.error("in '%s' __TEXT segment not start of mach-o", path
); 
 383     if ( !diag
.noError() && !foundTEXT 
) { 
 384         diag
.error("in '%s' __TEXT segment not found", path
); 
 391 const MachOAnalyzer
* MachOAnalyzer::remapIfZeroFill(Diagnostics
& diag
, const closure::FileSystem
& fileSystem
, closure::LoadedFileInfo
& info
) const 
 393     uint64_t vmSpaceRequired
; 
 395     analyzeSegmentsLayout(vmSpaceRequired
, hasZeroFill
); 
 398         vm_address_t newMappedAddr
; 
 399         if ( ::vm_allocate(mach_task_self(), &newMappedAddr
, (size_t)vmSpaceRequired
, VM_FLAGS_ANYWHERE
) != 0 ) { 
 400             diag
.error("vm_allocate failure"); 
 404         // re-map each segment read-only, with runtime layout 
 405 #if BUILDING_APP_CACHE_UTIL 
 406         // The auxKC is mapped with __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest 
 407         __block 
uint64_t baseAddress 
= ~0ULL; 
 408         forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
 409             baseAddress 
= std::min(baseAddress
, info
.vmAddr
); 
 411         uint64_t textSegVMAddr 
= preferredLoadAddress(); 
 413         uint64_t baseAddress 
= preferredLoadAddress(); 
 416         forEachSegment(^(const SegmentInfo
& segmentInfo
, bool& stop
) { 
 417             if ( (segmentInfo
.fileSize 
!= 0) && (segmentInfo
.vmSize 
!= 0) ) { 
 418                 kern_return_t r 
= vm_copy(mach_task_self(), (vm_address_t
)((long)info
.fileContent
+segmentInfo
.fileOffset
), (vm_size_t
)segmentInfo
.fileSize
, (vm_address_t
)(newMappedAddr
+segmentInfo
.vmAddr
-baseAddress
)); 
 419                 if ( r 
!= KERN_SUCCESS 
) { 
 420                     diag
.error("vm_copy() failure"); 
 425         if ( diag
.noError() ) { 
 426             // remove original mapping and return new mapping 
 427             fileSystem
.unloadFile(info
); 
 429             // make the new mapping read-only 
 430             ::vm_protect(mach_task_self(), newMappedAddr
, (vm_size_t
)vmSpaceRequired
, false, VM_PROT_READ
); 
 432 #if BUILDING_APP_CACHE_UTIL 
 433             if ( textSegVMAddr 
!= baseAddress 
) { 
 434                 info
.unload 
= [](const closure::LoadedFileInfo
& info
) { 
 435                     // Unloading binaries where __DATA is first requires working out the real range of the binary 
 436                     // The fileContent points at the mach_header, not the actaul start of the file content, unfortunately. 
 437                     const MachOAnalyzer
* ma 
= (const MachOAnalyzer
*)info
.fileContent
; 
 438                     __block 
uint64_t baseAddress 
= ~0ULL; 
 439                     ma
->forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
 440                         baseAddress 
= std::min(baseAddress
, info
.vmAddr
); 
 442                     uint64_t textSegVMAddr 
= ma
->preferredLoadAddress(); 
 444                     uint64_t basePointerOffset 
= textSegVMAddr 
- baseAddress
; 
 445                     uint8_t* bufferStart 
= (uint8_t*)info
.fileContent 
- basePointerOffset
; 
 446                     ::vm_deallocate(mach_task_self(), (vm_address_t
)bufferStart
, (size_t)info
.fileContentLen
); 
 449                 // And update the file content to the new location 
 450                 info
.fileContent 
= (const void*)(newMappedAddr 
+ textSegVMAddr 
- baseAddress
); 
 451                 info
.fileContentLen 
= vmSpaceRequired
; 
 452                 return (const MachOAnalyzer
*)info
.fileContent
; 
 456             // Set vm_deallocate as the unload method. 
 457             info
.unload 
= [](const closure::LoadedFileInfo
& info
) { 
 458                 ::vm_deallocate(mach_task_self(), (vm_address_t
)info
.fileContent
, (size_t)info
.fileContentLen
); 
 461             // And update the file content to the new location 
 462             info
.fileContent 
= (const void*)newMappedAddr
; 
 463             info
.fileContentLen 
= vmSpaceRequired
; 
 464             return (const MachOAnalyzer
*)info
.fileContent
; 
 467             // new mapping failed, return old mapping with an error in diag 
 468             ::vm_deallocate(mach_task_self(), newMappedAddr
, (size_t)vmSpaceRequired
); 
 476 void MachOAnalyzer::analyzeSegmentsLayout(uint64_t& vmSpace
, bool& hasZeroFill
) const 
 478     __block 
bool     writeExpansion 
= false; 
 479     __block 
uint64_t lowestVmAddr   
= 0xFFFFFFFFFFFFFFFFULL
; 
 480     __block 
uint64_t highestVmAddr  
= 0; 
 481     __block 
uint64_t sumVmSizes     
= 0; 
 482     forEachSegment(^(const SegmentInfo
& segmentInfo
, bool& stop
) { 
 483         if ( strcmp(segmentInfo
.segName
, "__PAGEZERO") == 0 ) 
 485         if ( segmentInfo
.writable() && (segmentInfo
.fileSize 
!=  segmentInfo
.vmSize
) ) 
 486             writeExpansion 
= true; // zerofill at end of __DATA 
 487         if ( segmentInfo
.vmSize 
== 0 ) { 
 488             // Always zero fill if we have zero-sized segments 
 489             writeExpansion 
= true; 
 491         if ( segmentInfo
.vmAddr 
< lowestVmAddr 
) 
 492             lowestVmAddr 
= segmentInfo
.vmAddr
; 
 493         if ( segmentInfo
.vmAddr
+segmentInfo
.vmSize 
> highestVmAddr 
) 
 494             highestVmAddr 
= segmentInfo
.vmAddr
+segmentInfo
.vmSize
; 
 495         sumVmSizes 
+= segmentInfo
.vmSize
; 
 497     uint64_t totalVmSpace 
= (highestVmAddr 
- lowestVmAddr
); 
 498     // LINKEDIT vmSize is not required to be a multiple of page size.  Round up if that is the case 
 499     const uint64_t pageSize 
= uses16KPages() ? 0x4000 : 0x1000; 
 500     totalVmSpace 
= (totalVmSpace 
+ (pageSize 
- 1)) & ~(pageSize 
- 1); 
 501     bool hasHole 
= (totalVmSpace 
!= sumVmSizes
); // segments not contiguous 
 503     // The aux KC may have __DATA first, in which case we always want to vm_copy to the right place 
 504     bool hasOutOfOrderSegments 
= false; 
 505 #if BUILDING_APP_CACHE_UTIL 
 506     uint64_t textSegVMAddr 
= preferredLoadAddress(); 
 507     hasOutOfOrderSegments 
= textSegVMAddr 
!= lowestVmAddr
; 
 510     vmSpace     
= totalVmSpace
; 
 511     hasZeroFill 
= writeExpansion 
|| hasHole 
|| hasOutOfOrderSegments
; 
 514 bool MachOAnalyzer::enforceFormat(Malformed kind
) const 
 516 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC 
 517     // HACK: If we are the kernel, we have a different format to enforce 
 521         case Malformed::linkeditOrder
: 
 522         case Malformed::linkeditAlignment
: 
 523         case Malformed::dyldInfoAndlocalRelocs
: 
 526         case Malformed::segmentOrder
: 
 527         // The aux KC has __DATA first 
 530         case Malformed::linkeditPermissions
: 
 531         case Malformed::executableData
: 
 532         case Malformed::writableData
: 
 533         case Malformed::codeSigAlignment
: 
 534         case Malformed::sectionsAddrRangeWithinSegment
: 
 537         case Malformed::textPermissions
: 
 538             // The kernel has its own __TEXT_EXEC for executable memory 
 545     if ( isStaticExecutable() ) { 
 548         case Malformed::linkeditOrder
: 
 549         case Malformed::linkeditAlignment
: 
 550         case Malformed::dyldInfoAndlocalRelocs
: 
 553         case Malformed::segmentOrder
: 
 556         case Malformed::linkeditPermissions
: 
 557         case Malformed::executableData
: 
 558         case Malformed::codeSigAlignment
: 
 559         case Malformed::textPermissions
: 
 560         case Malformed::sectionsAddrRangeWithinSegment
: 
 563         case Malformed::writableData
: 
 564             // The kernel has __DATA_CONST marked as r/o 
 573     __block 
bool result 
= false; 
 574     forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) { 
 576         case Platform::macOS
: 
 578             case Malformed::linkeditOrder
: 
 579             case Malformed::linkeditAlignment
: 
 580             case Malformed::dyldInfoAndlocalRelocs
: 
 581                 // enforce these checks on new binaries only 
 582                 if (sdk 
>= 0x000A0E00) // macOS 10.14 
 585             case Malformed::segmentOrder
: 
 586             case Malformed::linkeditPermissions
: 
 587             case Malformed::textPermissions
: 
 588             case Malformed::executableData
: 
 589             case Malformed::writableData
: 
 590             case Malformed::codeSigAlignment
: 
 591                 // enforce these checks on new binaries only 
 592                 if (sdk 
>= 0x000A0F00) // macOS 10.15 
 595             case Malformed::sectionsAddrRangeWithinSegment
: 
 596                 // enforce these checks on new binaries only 
 597                 if (sdk 
>= 0x000A1000) // macOS 10.16 
 604             case Malformed::linkeditOrder
: 
 605             case Malformed::dyldInfoAndlocalRelocs
: 
 606             case Malformed::textPermissions
: 
 607             case Malformed::executableData
: 
 608             case Malformed::writableData
: 
 611             case Malformed::linkeditAlignment
: 
 612             case Malformed::segmentOrder
: 
 613             case Malformed::linkeditPermissions
: 
 614             case Malformed::codeSigAlignment
: 
 615                 // enforce these checks on new binaries only 
 616                 if (sdk 
>= 0x000D0000) // iOS 13 
 619             case Malformed::sectionsAddrRangeWithinSegment
: 
 620                 // enforce these checks on new binaries only 
 621                 if (sdk 
>= 0x000E0000) // iOS 14 
 631     // if binary is so old, there is no platform info, don't enforce malformed errors 
 635 bool MachOAnalyzer::validEmbeddedPaths(Diagnostics
& diag
, Platform platform
, const char* path
) const 
 637     __block 
int  index 
= 1; 
 638     __block 
bool allGood 
= true; 
 639     __block 
bool foundInstallName 
= false; 
 640     __block 
int  dependentsCount 
= 0; 
 641     forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { 
 642         const dylib_command
* dylibCmd
; 
 643         const rpath_command
* rpathCmd
; 
 644         switch ( cmd
->cmd 
) { 
 646                 foundInstallName 
= true; 
 648                 [[clang::fallthrough]]; 
 650             case LC_LOAD_WEAK_DYLIB
: 
 651             case LC_REEXPORT_DYLIB
: 
 652             case LC_LOAD_UPWARD_DYLIB
: 
 653                 dylibCmd 
= (dylib_command
*)cmd
; 
 654                 if ( dylibCmd
->dylib
.name
.offset 
> cmd
->cmdsize 
) { 
 655                     diag
.error("in '%s' load command #%d name offset (%u) outside its size (%u)", path
, index
, dylibCmd
->dylib
.name
.offset
, cmd
->cmdsize
); 
 660                     bool foundEnd 
= false; 
 661                     const char* start 
= (char*)dylibCmd 
+ dylibCmd
->dylib
.name
.offset
; 
 662                     const char* end   
= (char*)dylibCmd 
+ cmd
->cmdsize
; 
 663                     for (const char* s
=start
; s 
< end
; ++s
) { 
 670                         diag
.error("in '%s' load command #%d string extends beyond end of load command", path
, index
); 
 675                 if ( cmd
->cmd  
!= LC_ID_DYLIB 
) 
 679                 rpathCmd 
= (rpath_command
*)cmd
; 
 680                 if ( rpathCmd
->path
.offset 
> cmd
->cmdsize 
) { 
 681                     diag
.error("in '%s' load command #%d path offset (%u) outside its size (%u)", path
, index
, rpathCmd
->path
.offset
, cmd
->cmdsize
); 
 686                     bool foundEnd 
= false; 
 687                     const char* start 
= (char*)rpathCmd 
+ rpathCmd
->path
.offset
; 
 688                     const char* end   
= (char*)rpathCmd 
+ cmd
->cmdsize
; 
 689                     for (const char* s
=start
; s 
< end
; ++s
) { 
 696                         diag
.error("in '%s' load command #%d string extends beyond end of load command", path
, index
); 
 708     if ( this->filetype 
== MH_DYLIB 
) { 
 709         if ( !foundInstallName 
) { 
 710             diag
.error("in '%s' MH_DYLIB is missing LC_ID_DYLIB", path
); 
 715         if ( foundInstallName 
) { 
 716             diag
.error("in '%s' LC_ID_DYLIB found in non-MH_DYLIB", path
); 
 721     if ( (dependentsCount 
== 0) && (this->filetype 
== MH_EXECUTE
) && isDynamicExecutable() ) { 
 722         diag
.error("in '%s' missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)", path
); 
 729 bool MachOAnalyzer::validSegments(Diagnostics
& diag
, const char* path
, size_t fileLen
) const 
 731     // check segment load command size 
 732     __block 
bool badSegmentLoadCommand 
= false; 
 733     forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { 
 734         if ( cmd
->cmd 
== LC_SEGMENT_64 
) { 
 735             const segment_command_64
* seg 
= (segment_command_64
*)cmd
; 
 736             int32_t sectionsSpace 
= cmd
->cmdsize 
- sizeof(segment_command_64
); 
 737             if ( sectionsSpace 
< 0 ) { 
 738                diag
.error("in '%s' load command size too small for LC_SEGMENT_64", path
); 
 739                badSegmentLoadCommand 
= true; 
 742             else if ( (sectionsSpace 
% sizeof(section_64
)) != 0 ) { 
 743                diag
.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path
, cmd
->cmdsize
); 
 744                badSegmentLoadCommand 
= true; 
 747             else if ( sectionsSpace 
!= (seg
->nsects 
* sizeof(section_64
)) ) { 
 748                diag
.error("in '%s' load command size 0x%X does not match nsects %d", path
, cmd
->cmdsize
, seg
->nsects
); 
 749                badSegmentLoadCommand 
= true; 
 752             else if ( greaterThanAddOrOverflow(seg
->fileoff
, seg
->filesize
, fileLen
) ) { 
 753                 diag
.error("in '%s' segment load command content extends beyond end of file", path
); 
 754                 badSegmentLoadCommand 
= true; 
 757             else if ( (seg
->filesize 
> seg
->vmsize
) && ((seg
->vmsize 
!= 0) || ((seg
->flags 
& SG_NORELOC
) == 0)) ) { 
 758                 // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment 
 759                 diag
.error("in '%s' segment filesize exceeds vmsize", path
); 
 760                 badSegmentLoadCommand 
= true; 
 764         else if ( cmd
->cmd 
== LC_SEGMENT 
) { 
 765             const segment_command
* seg 
= (segment_command
*)cmd
; 
 766             int32_t sectionsSpace 
= cmd
->cmdsize 
- sizeof(segment_command
); 
 767             if ( sectionsSpace 
< 0 ) { 
 768                diag
.error("in '%s' load command size too small for LC_SEGMENT", path
); 
 769                badSegmentLoadCommand 
= true; 
 772             else if ( (sectionsSpace 
% sizeof(section
)) != 0 ) { 
 773                diag
.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path
, cmd
->cmdsize
); 
 774                badSegmentLoadCommand 
= true; 
 777             else if ( sectionsSpace 
!= (seg
->nsects 
* sizeof(section
)) ) { 
 778                diag
.error("in '%s' load command size 0x%X does not match nsects %d", path
, cmd
->cmdsize
, seg
->nsects
); 
 779                badSegmentLoadCommand 
= true; 
 782             else if ( (seg
->filesize 
> seg
->vmsize
) && ((seg
->vmsize 
!= 0) || ((seg
->flags 
& SG_NORELOC
) == 0)) ) { 
 783                 // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment 
 784                 diag
.error("in '%s' segment filesize exceeds vmsize", path
); 
 785                 badSegmentLoadCommand 
= true; 
 790      if ( badSegmentLoadCommand 
) 
 793     // check mapping permissions of segments 
 794     __block 
bool badPermissions 
= false; 
 795     __block 
bool badSize        
= false; 
 796     __block 
bool hasTEXT        
= false; 
 797     __block 
bool hasLINKEDIT    
= false; 
 798     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
 799         if ( strcmp(info
.segName
, "__TEXT") == 0 ) { 
 800             if ( (info
.protections 
!= (VM_PROT_READ
|VM_PROT_EXECUTE
)) && enforceFormat(Malformed::textPermissions
) ) { 
 801                 diag
.error("in '%s' __TEXT segment permissions is not 'r-x'", path
); 
 802                 badPermissions 
= true; 
 807         else if ( strcmp(info
.segName
, "__LINKEDIT") == 0 ) { 
 808             if ( (info
.protections 
!= VM_PROT_READ
) && enforceFormat(Malformed::linkeditPermissions
) ) { 
 809                 diag
.error("in '%s' __LINKEDIT segment permissions is not 'r--'", path
); 
 810                 badPermissions 
= true; 
 815         else if ( (info
.protections 
& 0xFFFFFFF8) != 0 ) { 
 816             diag
.error("in '%s' %s segment permissions has invalid bits set", path
, info
.segName
); 
 817             badPermissions 
= true; 
 820         if ( greaterThanAddOrOverflow(info
.fileOffset
, info
.fileSize
, fileLen
) ) { 
 821             diag
.error("in '%s' %s segment content extends beyond end of file", path
, info
.segName
); 
 826             if ( info
.vmAddr
+info
.vmSize 
< info
.vmAddr 
) { 
 827                 diag
.error("in '%s' %s segment vm range wraps", path
, info
.segName
); 
 833             if ( (uint32_t)(info
.vmAddr
+info
.vmSize
) < (uint32_t)(info
.vmAddr
) ) { 
 834                 diag
.error("in '%s' %s segment vm range wraps", path
, info
.segName
); 
 840     if ( badPermissions 
|| badSize 
) 
 843         diag
.error("in '%s' missing __TEXT segment", path
); 
 846     if ( !hasLINKEDIT 
) { 
 847        diag
.error("in '%s' missing __LINKEDIT segment", path
); 
 851     // check for overlapping segments 
 852     __block 
bool badSegments 
= false; 
 853     forEachSegment(^(const SegmentInfo
& info1
, bool& stop1
) { 
 854         uint64_t seg1vmEnd   
= info1
.vmAddr 
+ info1
.vmSize
; 
 855         uint64_t seg1FileEnd 
= info1
.fileOffset 
+ info1
.fileSize
; 
 856         forEachSegment(^(const SegmentInfo
& info2
, bool& stop2
) { 
 857             if ( info1
.segIndex 
== info2
.segIndex 
) 
 859             uint64_t seg2vmEnd   
= info2
.vmAddr 
+ info2
.vmSize
; 
 860             uint64_t seg2FileEnd 
= info2
.fileOffset 
+ info2
.fileSize
; 
 861             if ( ((info2
.vmAddr 
<= info1
.vmAddr
) && (seg2vmEnd 
> info1
.vmAddr
) && (seg1vmEnd 
> info1
.vmAddr 
)) || ((info2
.vmAddr 
>= info1
.vmAddr 
) && (info2
.vmAddr 
< seg1vmEnd
) && (seg2vmEnd 
> info2
.vmAddr
)) ) { 
 862                 diag
.error("in '%s' segment %s vm range overlaps segment %s", path
, info1
.segName
, info2
.segName
); 
 867              if ( ((info2
.fileOffset  
<= info1
.fileOffset
) && (seg2FileEnd 
> info1
.fileOffset
) && (seg1FileEnd 
> info1
.fileOffset
)) || ((info2
.fileOffset  
>= info1
.fileOffset
) && (info2
.fileOffset  
< seg1FileEnd
) && (seg2FileEnd 
> info2
.fileOffset 
)) ) { 
 868                 diag
.error("in '%s' segment %s file content overlaps segment %s", path
, info1
.segName
, info2
.segName
); 
 873             if ( (info1
.segIndex 
< info2
.segIndex
) && !stop1 
) { 
 874                 if ( (info1
.vmAddr 
> info2
.vmAddr
) || ((info1
.fileOffset 
> info2
.fileOffset 
) && (info1
.fileOffset 
!= 0) && (info2
.fileOffset  
!= 0)) ){ 
 875                     if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder
) && !isStaticExecutable() ) { 
 876                         // dyld cache __DATA_* segments are moved around 
 877                         // The static kernel also has segments with vmAddr's before __TEXT 
 878                         diag
.error("in '%s' segment load commands out of order with respect to layout for %s and %s", path
, info1
.segName
, info2
.segName
); 
 890     // check sections are within segment 
 891     __block 
bool badSections 
= false; 
 892     forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { 
 893         if ( cmd
->cmd 
== LC_SEGMENT_64 
) { 
 894             const segment_command_64
* seg 
= (segment_command_64
*)cmd
; 
 895             const section_64
* const sectionsStart 
= (section_64
*)((char*)seg 
+ sizeof(struct segment_command_64
)); 
 896             const section_64
* const sectionsEnd   
= §ionsStart
[seg
->nsects
]; 
 897             for (const section_64
* sect
=sectionsStart
; (sect 
< sectionsEnd
); ++sect
) { 
 898                 if ( (int64_t)(sect
->size
) < 0 ) { 
 899                     diag
.error("in '%s' section '%s' size too large 0x%llX", path
, sect
->sectname
, sect
->size
); 
 902                 else if ( sect
->addr 
< seg
->vmaddr 
) { 
 903                     diag
.error("in '%s' section '%s' start address 0x%llX is before containing segment's address 0x%0llX", path
, sect
->sectname
, sect
->addr
, seg
->vmaddr
); 
 906                 else if ( sect
->addr
+sect
->size 
> seg
->vmaddr
+seg
->vmsize 
) { 
 907                     bool ignoreError 
= !enforceFormat(Malformed::sectionsAddrRangeWithinSegment
); 
 908 #if BUILDING_APP_CACHE_UTIL 
 909                     if ( (seg
->vmsize 
== 0) && !strcmp(seg
->segname
, "__CTF") ) 
 912                     if ( !ignoreError 
) { 
 913                         diag
.error("in '%s' section '%s' end address 0x%llX is beyond containing segment's end address 0x%0llX", path
, sect
->sectname
, sect
->addr
+sect
->size
, seg
->vmaddr
+seg
->vmsize
); 
 919         else if ( cmd
->cmd 
== LC_SEGMENT 
) { 
 920             const segment_command
* seg 
= (segment_command
*)cmd
; 
 921             const section
* const sectionsStart 
= (section
*)((char*)seg 
+ sizeof(struct segment_command
)); 
 922             const section
* const sectionsEnd   
= §ionsStart
[seg
->nsects
]; 
 923             for (const section
* sect
=sectionsStart
; !stop 
&& (sect 
< sectionsEnd
); ++sect
) { 
 924                if ( (int64_t)(sect
->size
) < 0 ) { 
 925                     diag
.error("in '%s' section %s size too large 0x%X", path
, sect
->sectname
, sect
->size
); 
 928                 else if ( sect
->addr 
< seg
->vmaddr 
) { 
 929                     diag
.error("in '%s' section %s start address 0x%X is before containing segment's address 0x%0X", path
,  sect
->sectname
, sect
->addr
, seg
->vmaddr
); 
 932                 else if ( sect
->addr
+sect
->size 
> seg
->vmaddr
+seg
->vmsize 
) { 
 933                     diag
.error("in '%s' section %s end address 0x%X is beyond containing segment's end address 0x%0X", path
, sect
->sectname
, sect
->addr
+sect
->size
, seg
->vmaddr
+seg
->vmsize
); 
 944 bool MachOAnalyzer::validMain(Diagnostics
& diag
, const char* path
) const 
 946     const char* executableTextSegmentName 
= "__TEXT"; 
 947 #if BUILDING_APP_CACHE_UTIL 
 948     // The kernel has __start in __TEXT_EXEC, or for x86_64 it's __HIB 
 949     if ( isStaticExecutable() ) { 
 950         if ( isArch("x86_64") || isArch("x86_64h") ) 
 951             executableTextSegmentName 
= "__HIB"; 
 953             executableTextSegmentName 
= "__TEXT_EXEC"; 
 957     __block 
uint64_t textSegStartAddr 
= 0; 
 958     __block 
uint64_t textSegStartSize 
= 0; 
 959     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
 960         if ( strcmp(info
.segName
, executableTextSegmentName
) == 0 ) { 
 961             textSegStartAddr 
= info
.vmAddr
; 
 962             textSegStartSize 
= info
.vmSize
; 
 967     __block 
int mainCount   
= 0; 
 968     __block 
int threadCount 
= 0; 
 969     forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { 
 970         entry_point_command
* mainCmd
; 
 971         uint64_t startAddress
; 
 975                 mainCmd 
= (entry_point_command
*)cmd
; 
 976                 if ( mainCmd
->entryoff 
>= textSegStartSize 
) { 
 977                     startAddress 
= preferredLoadAddress() + mainCmd
->entryoff
; 
 978                     __block 
bool foundSegment 
= false; 
 979                     forEachSegment(^(const SegmentInfo
& info
, bool& stopSegment
) { 
 980                         // Skip segments which don't contain this address 
 981                         if ( (startAddress 
< info
.vmAddr
) || (startAddress 
>= info
.vmAddr
+info
.vmSize
) ) 
 984                         if ( (info
.protections 
& VM_PROT_EXECUTE
) == 0 ) 
 985                             diag
.error("LC_MAIN points to non-executable segment"); 
 989                         diag
.error("LC_MAIN entryoff is out of range"); 
 995                 startAddress 
= entryAddrFromThreadCmd((thread_command
*)cmd
); 
 996                 if ( startAddress 
== 0 ) { 
 997                     diag
.error("LC_UNIXTHREAD not valid for arch %s", archName()); 
1000 #if BUILDING_DYLDINFO 
1001                 else if ( isStaticExecutable() ) { 
1002                     __block 
bool foundSegment 
= false; 
1003                     forEachSegment(^(const SegmentInfo
& info
, bool& stopSegment
) { 
1004                         // Skip segments which don't contain this address 
1005                         if ( (startAddress 
< info
.vmAddr
) || (startAddress 
>= info
.vmAddr
+info
.vmSize
) ) 
1007                         foundSegment 
= true; 
1008                         if ( (info
.protections 
& VM_PROT_EXECUTE
) == 0 ) 
1009                             diag
.error("LC_UNIXTHREAD points to non-executable segment"); 
1013                         diag
.error("LC_UNIXTHREAD entry is out of range"); 
1017                 else if ( (startAddress 
< textSegStartAddr
) || (startAddress 
>= textSegStartAddr
+textSegStartSize
) ) { 
1018                     diag
.error("LC_UNIXTHREAD entry not in %s segment", executableTextSegmentName
); 
1024     if ( diag
.hasError() ) 
1027     if ( this->builtForPlatform(Platform::driverKit
) ) { 
1028         if ( mainCount 
+ threadCount 
== 0 ) 
1030         diag
.error("no LC_MAIN allowed for driverkit"); 
1034     if ( mainCount
+threadCount 
== 1 ) 
1037     if ( mainCount 
+ threadCount 
== 0 ) 
1038         diag
.error("missing LC_MAIN or LC_UNIXTHREAD"); 
1040         diag
.error("only one LC_MAIN or LC_UNIXTHREAD is allowed"); 
1046     struct LinkEditContentChunk
 
1050         uint32_t    fileOffsetStart
; 
1053         static int compareByFileOffset(const void* l
, const void* r
) { 
1054             if ( ((LinkEditContentChunk
*)l
)->fileOffsetStart 
< ((LinkEditContentChunk
*)r
)->fileOffsetStart 
) 
1060 } // anonymous namespace 
1064 bool MachOAnalyzer::validLinkeditLayout(Diagnostics
& diag
, const char* path
) const 
1066     LinkEditInfo leInfo
; 
1067     getLinkEditPointers(diag
, leInfo
); 
1068     if ( diag
.hasError() ) 
1070     const uint32_t ptrSize 
= pointerSize(); 
1072     // build vector of all blobs in LINKEDIT 
1073     LinkEditContentChunk blobs
[32]; 
1074     LinkEditContentChunk
* bp 
= blobs
; 
1075     if ( leInfo
.dyldInfo 
!= nullptr ) { 
1076         if ( leInfo
.dyldInfo
->rebase_size 
!= 0 ) 
1077             *bp
++ = {"rebase opcodes",          ptrSize
, leInfo
.dyldInfo
->rebase_off
, leInfo
.dyldInfo
->rebase_size
}; 
1078         if ( leInfo
.dyldInfo
->bind_size 
!= 0 ) 
1079             *bp
++ = {"bind opcodes",            ptrSize
, leInfo
.dyldInfo
->bind_off
, leInfo
.dyldInfo
->bind_size
}; 
1080         if ( leInfo
.dyldInfo
->weak_bind_size 
!= 0 ) 
1081             *bp
++ = {"weak bind opcodes",       ptrSize
, leInfo
.dyldInfo
->weak_bind_off
, leInfo
.dyldInfo
->weak_bind_size
}; 
1082         if ( leInfo
.dyldInfo
->lazy_bind_size 
!= 0 ) 
1083             *bp
++ = {"lazy bind opcodes",       ptrSize
, leInfo
.dyldInfo
->lazy_bind_off
, leInfo
.dyldInfo
->lazy_bind_size
}; 
1084         if ( leInfo
.dyldInfo
->export_size
!= 0 ) 
1085             *bp
++ = {"exports trie",            ptrSize
, leInfo
.dyldInfo
->export_off
, leInfo
.dyldInfo
->export_size
}; 
1087     if ( leInfo
.exportsTrie 
!= nullptr ) { 
1088         if ( leInfo
.exportsTrie
->datasize 
!= 0 ) 
1089             *bp
++ = {"exports trie",            ptrSize
, leInfo
.exportsTrie
->dataoff
, leInfo
.exportsTrie
->datasize
}; 
1091     if ( leInfo
.chainedFixups 
!= nullptr ) { 
1092         if ( leInfo
.chainedFixups
->datasize 
!= 0 ) 
1093             *bp
++ = {"chained fixups",          ptrSize
, leInfo
.chainedFixups
->dataoff
, leInfo
.chainedFixups
->datasize
}; 
1096     if ( leInfo
.dynSymTab 
!= nullptr ) { 
1097         if ( leInfo
.dynSymTab
->nlocrel 
!= 0 ) 
1098             *bp
++ = {"local relocations",       ptrSize
, leInfo
.dynSymTab
->locreloff
, static_cast<uint32_t>(leInfo
.dynSymTab
->nlocrel
*sizeof(relocation_info
))}; 
1099         if ( leInfo
.dynSymTab
->nextrel 
!= 0 ) 
1100             *bp
++ = {"external relocations",    ptrSize
, leInfo
.dynSymTab
->extreloff
, static_cast<uint32_t>(leInfo
.dynSymTab
->nextrel
*sizeof(relocation_info
))}; 
1101         if ( leInfo
.dynSymTab
->nindirectsyms 
!= 0 ) 
1102             *bp
++ = {"indirect symbol table",   4,       leInfo
.dynSymTab
->indirectsymoff
, leInfo
.dynSymTab
->nindirectsyms
*4}; 
1104     if ( leInfo
.splitSegInfo 
!= nullptr ) { 
1105         if ( leInfo
.splitSegInfo
->datasize 
!= 0 ) 
1106             *bp
++ = {"shared cache info",       ptrSize
, leInfo
.splitSegInfo
->dataoff
, leInfo
.splitSegInfo
->datasize
}; 
1108     if ( leInfo
.functionStarts 
!= nullptr ) { 
1109         if ( leInfo
.functionStarts
->datasize 
!= 0 ) 
1110             *bp
++ = {"function starts",         ptrSize
, leInfo
.functionStarts
->dataoff
, leInfo
.functionStarts
->datasize
}; 
1112     if ( leInfo
.dataInCode 
!= nullptr ) { 
1113         if ( leInfo
.dataInCode
->datasize 
!= 0 ) 
1114             *bp
++ = {"data in code",            ptrSize
, leInfo
.dataInCode
->dataoff
, leInfo
.dataInCode
->datasize
}; 
1116     if ( leInfo
.symTab 
!= nullptr ) { 
1117         if ( leInfo
.symTab
->nsyms 
!= 0 ) 
1118             *bp
++ = {"symbol table",            ptrSize
, leInfo
.symTab
->symoff
, static_cast<uint32_t>(leInfo
.symTab
->nsyms
*(ptrSize 
== 8 ? sizeof(nlist_64
) : sizeof(struct nlist
)))}; 
1119         if ( leInfo
.symTab
->strsize 
!= 0 ) 
1120             *bp
++ = {"symbol table strings",    1,       leInfo
.symTab
->stroff
, leInfo
.symTab
->strsize
}; 
1122     if ( leInfo
.codeSig 
!= nullptr ) { 
1123         if ( leInfo
.codeSig
->datasize 
!= 0 ) 
1124             *bp
++ = {"code signature",          ptrSize
, leInfo
.codeSig
->dataoff
, leInfo
.codeSig
->datasize
}; 
1127     // check for bad combinations 
1128     if ( (leInfo
.dyldInfo 
!= nullptr) && (leInfo
.dyldInfo
->cmd 
== LC_DYLD_INFO_ONLY
) && (leInfo
.dynSymTab 
!= nullptr) ) { 
1129         if ( (leInfo
.dynSymTab
->nlocrel 
!= 0) && enforceFormat(Malformed::dyldInfoAndlocalRelocs
) ) { 
1130             diag
.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations", path
); 
1133         if ( leInfo
.dynSymTab
->nextrel 
!= 0 ) { 
1134             diag
.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations", path
); 
1139     bool checkMissingDyldInfo 
= true; 
1140 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL 
1141     checkMissingDyldInfo 
= !isFileSet() && !isStaticExecutable() && !isKextBundle(); 
1143     if ( (leInfo
.dyldInfo 
== nullptr) && (leInfo
.dynSymTab 
== nullptr) && checkMissingDyldInfo 
) { 
1144         diag
.error("in '%s' malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB", path
); 
1148     // FIXME: Remove this hack 
1149 #if BUILDING_APP_CACHE_UTIL 
1154     const unsigned long blobCount 
= bp 
- blobs
; 
1155     if ( blobCount 
== 0 ) { 
1156         diag
.error("in '%s' malformed mach-o missing LINKEDIT", path
); 
1160     uint32_t linkeditFileEnd 
= leInfo
.layout
.linkeditFileOffset 
+ leInfo
.layout
.linkeditFileSize
; 
1163     // sort blobs by file-offset and error on overlaps 
1164     ::qsort(blobs
, blobCount
, sizeof(LinkEditContentChunk
), &LinkEditContentChunk::compareByFileOffset
); 
1165     uint32_t     prevEnd 
= leInfo
.layout
.linkeditFileOffset
; 
1166     const char*  prevName 
= "start of LINKEDIT"; 
1167     for (unsigned long i
=0; i 
< blobCount
; ++i
) { 
1168         const LinkEditContentChunk
& blob 
= blobs
[i
]; 
1169         if ( blob
.fileOffsetStart 
< prevEnd 
) { 
1170             diag
.error("in '%s' LINKEDIT overlap of %s and %s", path
, prevName
, blob
.name
); 
1173         if (greaterThanAddOrOverflow(blob
.fileOffsetStart
, blob
.size
, linkeditFileEnd
)) { 
1174             diag
.error("in '%s' LINKEDIT content '%s' extends beyond end of segment", path
, blob
.name
); 
1177         if ( (blob
.fileOffsetStart 
& (blob
.alignment
-1)) != 0 ) { 
1178             // <rdar://problem/51115705> relax code sig alignment for pre iOS13 
1179             Malformed kind 
= (strcmp(blob
.name
, "code signature") == 0) ? Malformed::codeSigAlignment 
: Malformed::linkeditAlignment
; 
1180             if ( enforceFormat(kind
) ) 
1181                 diag
.error("in '%s' mis-aligned LINKEDIT content '%s'", path
, blob
.name
); 
1183         prevEnd  
= blob
.fileOffsetStart 
+ blob
.size
; 
1184         prevName 
= blob
.name
; 
1187     // Check for invalid symbol table sizes 
1188     if ( leInfo
.symTab 
!= nullptr ) { 
1189         if ( leInfo
.symTab
->nsyms 
> 0x10000000 ) { 
1190             diag
.error("in '%s' malformed mach-o image: symbol table too large", path
); 
1193         if ( leInfo
.dynSymTab 
!= nullptr ) { 
1194             // validate indirect symbol table 
1195             if ( leInfo
.dynSymTab
->nindirectsyms 
!= 0 ) { 
1196                 if ( leInfo
.dynSymTab
->nindirectsyms 
> 0x10000000 ) { 
1197                     diag
.error("in '%s' malformed mach-o image: indirect symbol table too large", path
); 
1201             if ( (leInfo
.dynSymTab
->nlocalsym 
> leInfo
.symTab
->nsyms
) || (leInfo
.dynSymTab
->ilocalsym 
> leInfo
.symTab
->nsyms
) ) { 
1202                 diag
.error("in '%s' malformed mach-o image: indirect symbol table local symbol count exceeds total symbols", path
); 
1205             if ( leInfo
.dynSymTab
->ilocalsym 
+ leInfo
.dynSymTab
->nlocalsym 
< leInfo
.dynSymTab
->ilocalsym  
) { 
1206                 diag
.error("in '%s' malformed mach-o image: indirect symbol table local symbol count wraps", path
); 
1209             if ( (leInfo
.dynSymTab
->nextdefsym 
> leInfo
.symTab
->nsyms
) || (leInfo
.dynSymTab
->iextdefsym 
> leInfo
.symTab
->nsyms
) ) { 
1210                 diag
.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols", path
); 
1213             if ( leInfo
.dynSymTab
->iextdefsym 
+ leInfo
.dynSymTab
->nextdefsym 
< leInfo
.dynSymTab
->iextdefsym  
) { 
1214                 diag
.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count wraps", path
); 
1217             if ( (leInfo
.dynSymTab
->nundefsym 
> leInfo
.symTab
->nsyms
) || (leInfo
.dynSymTab
->iundefsym 
> leInfo
.symTab
->nsyms
) ) { 
1218                 diag
.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols", path
); 
1221             if ( leInfo
.dynSymTab
->iundefsym 
+ leInfo
.dynSymTab
->nundefsym 
< leInfo
.dynSymTab
->iundefsym  
) { 
1222                 diag
.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count wraps", path
); 
1233 bool MachOAnalyzer::invalidRebaseState(Diagnostics
& diag
, const char* opcodeName
, const char* path
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1234                                       bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, Rebase kind
) const 
1236     if ( !segIndexSet 
) { 
1237         diag
.error("in '%s' %s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path
, opcodeName
); 
1240     if ( segmentIndex 
>= leInfo
.layout
.linkeditSegIndex 
)  { 
1241         diag
.error("in '%s' %s segment index %d too large", path
, opcodeName
, segmentIndex
); 
1244     if ( segmentOffset 
> (segments
[segmentIndex
].vmSize
-ptrSize
) ) { 
1245         diag
.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path
, opcodeName
, segmentOffset
, segments
[segmentIndex
].vmSize
); 
1249         case Rebase::pointer32
: 
1250         case Rebase::pointer64
: 
1251             if ( !segments
[segmentIndex
].writable() && enforceFormat(Malformed::writableData
) ) { 
1252                 diag
.error("in '%s' %s pointer rebase is in non-writable segment", path
, opcodeName
); 
1255             if ( segments
[segmentIndex
].executable() && enforceFormat(Malformed::executableData
) ) { 
1256                 diag
.error("in '%s' %s pointer rebase is in executable segment", path
, opcodeName
); 
1260         case Rebase::textAbsolute32
: 
1261         case Rebase::textPCrel32
: 
1262             if ( !segments
[segmentIndex
].textRelocs 
) { 
1263                 diag
.error("in '%s' %s text rebase is in segment that does not support text relocations", path
, opcodeName
); 
1266             if ( segments
[segmentIndex
].writable() ) { 
1267                 diag
.error("in '%s' %s text rebase is in writable segment", path
, opcodeName
); 
1270             if ( !segments
[segmentIndex
].executable() ) { 
1271                 diag
.error("in '%s' %s pointer rebase is in non-executable segment", path
, opcodeName
); 
1275         case Rebase::unknown
: 
1276             diag
.error("in '%s' %s unknown rebase type", path
, opcodeName
); 
1283 void MachOAnalyzer::getAllSegmentsInfos(Diagnostics
& diag
, SegmentInfo segments
[]) const 
1285     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
1286         segments
[info
.segIndex
] = info
; 
1291 bool MachOAnalyzer::validRebaseInfo(Diagnostics
& diag
, const char* path
) const 
1293     forEachRebase(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1294                           bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, Rebase kind
, bool& stop
) { 
1295         if ( invalidRebaseState(diag
, opcodeName
, path
, leInfo
, segments
, segIndexSet
, ptrSize
, segmentIndex
, segmentOffset
, kind
) ) 
1298     return diag
.noError(); 
1302 void MachOAnalyzer::forEachTextRebase(Diagnostics
& diag
, void (^handler
)(uint64_t runtimeOffset
, bool& stop
)) const 
1304     __block 
bool     startVmAddrSet 
= false; 
1305     __block 
uint64_t startVmAddr    
= 0; 
1306     forEachRebase(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1307                           bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, Rebase kind
, bool& stop
) { 
1308         if ( kind 
!= Rebase::textAbsolute32 
) 
1310         if ( !startVmAddrSet 
) { 
1311             for (int i
=0; i 
<= segmentIndex
; ++i
) { 
1312                 if ( strcmp(segments
[i
].segName
, "__TEXT") == 0 ) { 
1313                     startVmAddr 
= segments
[i
].vmAddr
; 
1314                     startVmAddrSet 
= true; 
1319         uint64_t rebaseVmAddr  
= segments
[segmentIndex
].vmAddr 
+ segmentOffset
; 
1320         uint64_t runtimeOffset 
= rebaseVmAddr 
- startVmAddr
; 
1321         handler(runtimeOffset
, stop
); 
1325 void MachOAnalyzer::forEachRebase(Diagnostics
& diag
, void (^callback
)(uint64_t runtimeOffset
, bool isLazyPointerRebase
, bool& stop
)) const 
1327     __block 
bool     startVmAddrSet 
= false; 
1328     __block 
uint64_t startVmAddr    
= 0; 
1329     __block 
uint64_t lpVmAddr       
= 0; 
1330     __block 
uint64_t lpEndVmAddr    
= 0; 
1331     __block 
uint64_t shVmAddr       
= 0; 
1332     __block 
uint64_t shEndVmAddr    
= 0; 
1333     forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) { 
1334         if ( (info
.sectFlags 
& SECTION_TYPE
) == S_LAZY_SYMBOL_POINTERS 
) { 
1335             lpVmAddr    
= info
.sectAddr
; 
1336             lpEndVmAddr 
= info
.sectAddr 
+ info
.sectSize
; 
1338         else if ( (info
.sectFlags 
& S_ATTR_PURE_INSTRUCTIONS
) && (strcmp(info
.sectName
, "__stub_helper") == 0) ) { 
1339             shVmAddr    
= info
.sectAddr
; 
1340             shEndVmAddr 
= info
.sectAddr 
+ info
.sectSize
; 
1343     forEachRebase(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1344                           bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, Rebase kind
, bool& stop
) { 
1346             case Rebase::unknown
: 
1348             case Rebase::pointer32
: 
1349             case Rebase::pointer64
: 
1350                 // We only handle these kinds for now. 
1352             case Rebase::textPCrel32
: 
1353             case Rebase::textAbsolute32
: 
1356         if ( !startVmAddrSet 
) { 
1357             for (int i
=0; i 
< segmentIndex
; ++i
) { 
1358                 if ( strcmp(segments
[i
].segName
, "__TEXT") == 0 ) { 
1359                     startVmAddr 
= segments
[i
].vmAddr
; 
1360                     startVmAddrSet 
= true; 
1365         uint64_t rebaseVmAddr  
= segments
[segmentIndex
].vmAddr 
+ segmentOffset
; 
1366         bool isLazyPointerRebase 
= false; 
1367         if ( (rebaseVmAddr 
>= lpVmAddr
) && (rebaseVmAddr 
< lpEndVmAddr
) ) { 
1368             // rebase is in lazy pointer section 
1369             uint64_t lpValue 
= 0; 
1371                 lpValue 
= *((uint64_t*)(rebaseVmAddr
-startVmAddr
+(uint8_t*)this)); 
1373                 lpValue 
= *((uint32_t*)(rebaseVmAddr
-startVmAddr
+(uint8_t*)this)); 
1374             if ( (lpValue 
>= shVmAddr
) && (lpValue 
< shEndVmAddr
) ) { 
1375                 // content is into stub_helper section 
1376                 uint64_t lpTargetImageOffset 
= lpValue 
- startVmAddr
; 
1377                 const uint8_t* helperContent 
= (uint8_t*)this + lpTargetImageOffset
; 
1378                 bool isLazyStub 
= contentIsRegularStub(helperContent
); 
1379                 // ignore rebases for normal lazy pointers, but leave rebase for resolver helper stub 
1381                     isLazyPointerRebase 
= true; 
1384                 // if lazy pointer does not point into stub_helper, then it points to weak-def symbol and we need rebase 
1387         uint64_t runtimeOffset 
= rebaseVmAddr 
- startVmAddr
; 
1388         callback(runtimeOffset
, isLazyPointerRebase
, stop
); 
1394 void MachOAnalyzer::forEachRebase(Diagnostics
& diag
, bool ignoreLazyPointers
, void (^handler
)(uint64_t runtimeOffset
, bool& stop
)) const 
1396     forEachRebase(diag
, ^(uint64_t runtimeOffset
, bool isLazyPointerRebase
, bool& stop
) { 
1397         if ( isLazyPointerRebase 
&& ignoreLazyPointers 
) 
1399         handler(runtimeOffset
, stop
); 
1403 bool MachOAnalyzer::hasStompedLazyOpcodes() const 
1405     // if first eight bytes of lazy opcodes are zeros, then the opcodes have been stomped 
1406     bool result 
= false; 
1408     if ( const uint8_t* p 
= (uint8_t*)getLazyBindOpcodes(size
) ) { 
1411             memcpy(&content
, p
, 8); 
1420 bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent
) const 
1422     switch (this->cputype
) { 
1423         case CPU_TYPE_X86_64
: 
1424             return ( (helperContent
[0] == 0x68) && (helperContent
[5] == 0xE9) ); // push $xxx / JMP pcRel 
1426             return ( (helperContent
[0] == 0x68) && (helperContent
[5] == 0xFF) && (helperContent
[2] == 0x26) ); // push $xxx / JMP *pcRel 
1428             return ( (helperContent
[0] == 0x00) && (helperContent
[1] == 0xC0) && (helperContent
[2] == 0x9F) && (helperContent
[3] == 0xE5) ); // ldr  ip, [pc, #0] 
1429         case CPU_TYPE_ARM64
: 
1430             return ( (helperContent
[0] == 0x50) && (helperContent
[1] == 0x00) && (helperContent
[2] == 0x00) && (helperContent
[3] == 0x18) ); // ldr w16, L0 
1436 static int relocSorter(const void* l
, const void* r
) { 
1437     if ( ((relocation_info
*)l
)->r_address 
< ((relocation_info
*)r
)->r_address 
) 
1444 void MachOAnalyzer::forEachRebase(Diagnostics
& diag
, 
1445                                  void (^handler
)(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1446                                                  bool segIndexSet
, uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, 
1447                                                  Rebase kind
, bool& stop
)) const 
1449     LinkEditInfo leInfo
; 
1450     getLinkEditPointers(diag
, leInfo
); 
1451     if ( diag
.hasError() ) 
1454     BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1); 
1455     getAllSegmentsInfos(diag
, segmentsInfo
); 
1456     if ( diag
.hasError() ) 
1459     const Rebase pointerRebaseKind 
= is64() ? Rebase::pointer64 
: Rebase::pointer32
; 
1461     if ( leInfo
.dyldInfo 
!= nullptr ) { 
1462         const uint8_t* const start 
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->rebase_off
); 
1463         const uint8_t* const end   
= start 
+ leInfo
.dyldInfo
->rebase_size
; 
1464         const uint8_t* p           
= start
; 
1465         const uint32_t ptrSize     
= pointerSize(); 
1466         Rebase   kind 
= Rebase::unknown
; 
1468         uint64_t segOffset 
= 0; 
1471         bool     segIndexSet 
= false; 
1473         while ( !stop 
&& diag
.noError() && (p 
< end
) ) { 
1474             uint8_t immediate 
= *p 
& REBASE_IMMEDIATE_MASK
; 
1475             uint8_t opcode 
= *p 
& REBASE_OPCODE_MASK
; 
1478                 case REBASE_OPCODE_DONE
: 
1479                     if ( (end 
- p
) > 8 ) 
1480                         diag
.error("rebase opcodes terminated early at offset %d of %d", (int)(p
-start
), (int)(end
-start
)); 
1483                 case REBASE_OPCODE_SET_TYPE_IMM
: 
1484                     switch ( immediate 
) { 
1485                         case REBASE_TYPE_POINTER
: 
1486                             kind 
= pointerRebaseKind
; 
1488                         case REBASE_TYPE_TEXT_ABSOLUTE32
: 
1489                             kind 
= Rebase::textAbsolute32
; 
1491                         case REBASE_TYPE_TEXT_PCREL32
: 
1492                             kind 
= Rebase::textPCrel32
; 
1495                             kind 
= Rebase::unknown
; 
1499                 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
: 
1500                     segIndex 
= immediate
; 
1501                     segOffset 
= read_uleb128(diag
, p
, end
); 
1504                 case REBASE_OPCODE_ADD_ADDR_ULEB
: 
1505                     segOffset 
+= read_uleb128(diag
, p
, end
); 
1507                 case REBASE_OPCODE_ADD_ADDR_IMM_SCALED
: 
1508                     segOffset 
+= immediate
*ptrSize
; 
1510                 case REBASE_OPCODE_DO_REBASE_IMM_TIMES
: 
1511                     for (int i
=0; i 
< immediate
; ++i
) { 
1512                         handler("REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo
, segmentsInfo
, segIndexSet
, ptrSize
, segIndex
, segOffset
, kind
, stop
); 
1513                         segOffset 
+= ptrSize
; 
1518                 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES
: 
1519                     count 
= read_uleb128(diag
, p
, end
); 
1520                     for (uint32_t i
=0; i 
< count
; ++i
) { 
1521                         handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo
, segmentsInfo
, segIndexSet
, ptrSize
, segIndex
, segOffset
, kind
, stop
); 
1522                         segOffset 
+= ptrSize
; 
1527                 case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB
: 
1528                     handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo
, segmentsInfo
, segIndexSet
, ptrSize
, segIndex
, segOffset
, kind
, stop
); 
1529                     segOffset 
+= read_uleb128(diag
, p
, end
) + ptrSize
; 
1531                 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB
: 
1532                     count 
= read_uleb128(diag
, p
, end
); 
1533                     if ( diag
.hasError() ) 
1535                     skip 
= read_uleb128(diag
, p
, end
); 
1536                     for (uint32_t i
=0; i 
< count
; ++i
) { 
1537                         handler("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo
, segmentsInfo
, segIndexSet
, ptrSize
, segIndex
, segOffset
, kind
, stop
); 
1538                         segOffset 
+= skip 
+ ptrSize
; 
1544                     diag
.error("unknown rebase opcode 0x%02X", opcode
); 
1550     if ( leInfo
.chainedFixups 
!= nullptr ) { 
1551         // binary uses chained fixups, so do nothing 
1552         // The kernel collections need to support both chained and classic relocations 
1553         // If we are anything other than a kernel collection, then return here as we won't have 
1554         // anything else to do. 
1559     if ( leInfo
.dynSymTab 
!= nullptr ) { 
1560         // old binary, walk relocations 
1561         const uint64_t                  relocsStartAddress 
= localRelocBaseAddress(segmentsInfo
, leInfo
.layout
.linkeditSegIndex
); 
1562         const relocation_info
* const    relocsStart 
= (relocation_info
*)getLinkEditContent(leInfo
.layout
, leInfo
.dynSymTab
->locreloff
); 
1563         const relocation_info
* const    relocsEnd   
= &relocsStart
[leInfo
.dynSymTab
->nlocrel
]; 
1565         const uint8_t                   relocSize 
= (is64() ? 3 : 2); 
1566         const uint8_t                   ptrSize   
= pointerSize(); 
1567         STACK_ALLOC_OVERFLOW_SAFE_ARRAY(relocation_info
, relocs
, 2048); 
1568         for (const relocation_info
* reloc
=relocsStart
; (reloc 
< relocsEnd
) && !stop
; ++reloc
) { 
1569             if ( reloc
->r_length 
!= relocSize 
) { 
1570                 bool shouldEmitError 
= true; 
1571 #if BUILDING_APP_CACHE_UTIL 
1572                 if ( usesClassicRelocationsInKernelCollection() && (reloc
->r_length 
== 2) && (relocSize 
== 3) ) 
1573                     shouldEmitError 
= false; 
1575                 if ( shouldEmitError 
) { 
1576                     diag
.error("local relocation has wrong r_length"); 
1580             if ( reloc
->r_type 
!= 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA ==  ARM64_RELOC_UNSIGNED 
1581                 diag
.error("local relocation has wrong r_type"); 
1584             relocs
.push_back(*reloc
); 
1586         if ( !relocs
.empty() ) { 
1587             ::qsort(&relocs
[0], relocs
.count(), sizeof(relocation_info
), &relocSorter
); 
1588             for (relocation_info reloc 
: relocs
) { 
1589                 uint32_t addrOff 
= reloc
.r_address
; 
1590                 uint32_t segIndex  
= 0; 
1591                 uint64_t segOffset 
= 0; 
1593 #if BUILDING_APP_CACHE_UTIL 
1594                 // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be 
1596                 if ( isStaticExecutable() || isFileSet() ) { 
1597                     addr 
= relocsStartAddress 
+ (int32_t)addrOff
; 
1599                     addr 
= relocsStartAddress 
+ addrOff
; 
1602                 addr 
= relocsStartAddress 
+ addrOff
; 
1604                 if ( segIndexAndOffsetForAddress(addr
, segmentsInfo
, leInfo
.layout
.linkeditSegIndex
, segIndex
, segOffset
) ) { 
1605                     Rebase kind 
= (reloc
.r_length 
== 2) ? Rebase::pointer32 
: Rebase::pointer64
; 
1606                     if ( this->cputype 
== CPU_TYPE_I386 
) { 
1607                         if ( segmentsInfo
[segIndex
].executable() ) 
1608                             kind 
= Rebase::textAbsolute32
; 
1610                     handler("local relocation", leInfo
, segmentsInfo
, true, ptrSize
, segIndex
, segOffset
, kind
, stop
); 
1613                     diag
.error("local relocation has out of range r_address"); 
1618         // then process indirect symbols 
1619         forEachIndirectPointer(diag
, ^(uint64_t address
, bool bind
, int bindLibOrdinal
, 
1620                                        const char* bindSymbolName
, bool bindWeakImport
, bool bindLazy
, bool selfModifyingStub
, bool& indStop
) { 
1623             uint32_t segIndex  
= 0; 
1624             uint64_t segOffset 
= 0; 
1625             if ( segIndexAndOffsetForAddress(address
, segmentsInfo
, leInfo
.layout
.linkeditSegIndex
, segIndex
, segOffset
) ) { 
1626                 handler("local relocation", leInfo
, segmentsInfo
, true, ptrSize
, segIndex
, segOffset
, pointerRebaseKind
, indStop
); 
1629                 diag
.error("local relocation has out of range r_address"); 
1636 bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr
, const SegmentInfo segmentsInfos
[], uint32_t segCount
, uint32_t& segIndex
, uint64_t& segOffset
) const 
1638     for (uint32_t i
=0; i 
< segCount
; ++i
) { 
1639         if ( (segmentsInfos
[i
].vmAddr 
<= addr
) && (addr 
< segmentsInfos
[i
].vmAddr
+segmentsInfos
[i
].vmSize
) ) { 
1641             segOffset 
= addr 
- segmentsInfos
[i
].vmAddr
; 
1648 uint64_t MachOAnalyzer::localRelocBaseAddress(const SegmentInfo segmentsInfos
[], uint32_t segCount
) const 
1650     if ( isArch("x86_64") || isArch("x86_64h") ) { 
1651 #if BUILDING_APP_CACHE_UTIL 
1652         if ( isKextBundle() ) { 
1653             // for kext bundles the reloc base address starts at __TEXT segment 
1654             return segmentsInfos
[0].vmAddr
; 
1657         // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA) 
1658         for (uint32_t i
=0; i 
< segCount
; ++i
) { 
1659             if ( segmentsInfos
[i
].writable() ) 
1660                 return segmentsInfos
[i
].vmAddr
; 
1663     return segmentsInfos
[0].vmAddr
; 
1666 uint64_t MachOAnalyzer::externalRelocBaseAddress(const SegmentInfo segmentsInfos
[], uint32_t segCount
) const 
1668     // Dyld caches are too large for a raw r_address, so everything is an offset from the base address 
1669     if ( inDyldCache() ) { 
1670         return preferredLoadAddress(); 
1673 #if BUILDING_APP_CACHE_UTIL 
1674     if ( isKextBundle() ) { 
1675         // for kext bundles the reloc base address starts at __TEXT segment 
1676         return preferredLoadAddress(); 
1680     if ( isArch("x86_64") || isArch("x86_64h") ) { 
1681         // for x86_64 reloc base address starts at first writable segment (usually __DATA) 
1682         for (uint32_t i
=0; i 
< segCount
; ++i
) { 
1683             if ( segmentsInfos
[i
].writable() ) 
1684                 return segmentsInfos
[i
].vmAddr
; 
1687     // For everyone else we start at 0 
1693 void MachOAnalyzer::forEachIndirectPointer(Diagnostics
& diag
, void (^handler
)(uint64_t pointerAddress
, bool bind
, int bindLibOrdinal
, const char* bindSymbolName
,  
1694                                                                              bool bindWeakImport
, bool bindLazy
, bool selfModifyingStub
, bool& stop
)) const 
1696     LinkEditInfo leInfo
; 
1697     getLinkEditPointers(diag
, leInfo
); 
1698     if ( diag
.hasError() ) 
1701     // find lazy and non-lazy pointer sections 
1702     const bool              is64Bit                  
= is64(); 
1703     const uint32_t* const   indirectSymbolTable      
= (uint32_t*)getLinkEditContent(leInfo
.layout
, leInfo
.dynSymTab
->indirectsymoff
); 
1704     const uint32_t          indirectSymbolTableCount 
= leInfo
.dynSymTab
->nindirectsyms
; 
1705     const uint32_t          ptrSize                  
= pointerSize(); 
1706     const void*             symbolTable              
= getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->symoff
); 
1707     const struct nlist_64
*  symbols64                
= (nlist_64
*)symbolTable
; 
1708     const struct nlist
*     symbols32                
= (struct nlist
*)symbolTable
; 
1709     const char*             stringPool               
= (char*)getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->stroff
); 
1710     uint32_t                symCount                 
= leInfo
.symTab
->nsyms
; 
1711     uint32_t                poolSize                 
= leInfo
.symTab
->strsize
; 
1712     __block 
bool            stop                     
= false; 
1714     // Old kexts put S_LAZY_SYMBOL_POINTERS on the __got section, even if they didn't have indirect symbols to prcess. 
1715     // In that case, skip the loop as there shouldn't be anything to process 
1716     if ( (indirectSymbolTableCount 
== 0) && isKextBundle() ) 
1719     forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& sectionStop
) { 
1720         uint8_t  sectionType  
= (sectInfo
.sectFlags 
& SECTION_TYPE
); 
1721         bool selfModifyingStub 
= (sectionType 
== S_SYMBOL_STUBS
) && (sectInfo
.sectFlags 
& S_ATTR_SELF_MODIFYING_CODE
) && (sectInfo
.reserved2 
== 5) && (this->cputype 
== CPU_TYPE_I386
); 
1722         if ( (sectionType 
!= S_LAZY_SYMBOL_POINTERS
) && (sectionType 
!= S_NON_LAZY_SYMBOL_POINTERS
) && !selfModifyingStub 
) 
1724         if ( (flags 
& S_ATTR_SELF_MODIFYING_CODE
) && !selfModifyingStub 
) { 
1725             diag
.error("S_ATTR_SELF_MODIFYING_CODE section type only valid in old i386 binaries"); 
1729         uint32_t elementSize 
= selfModifyingStub 
? sectInfo
.reserved2 
: ptrSize
; 
1730         uint32_t elementCount 
= (uint32_t)(sectInfo
.sectSize
/elementSize
); 
1731         if ( greaterThanAddOrOverflow(sectInfo
.reserved1
, elementCount
, indirectSymbolTableCount
) ) { 
1732             diag
.error("section %s overflows indirect symbol table", sectInfo
.sectName
); 
1737         for (uint32_t i
=0; (i 
< elementCount
) && !stop
; ++i
) { 
1738             uint32_t symNum 
= indirectSymbolTable
[sectInfo
.reserved1 
+ i
]; 
1739             if ( symNum 
== INDIRECT_SYMBOL_ABS 
) 
1741             if ( symNum 
== INDIRECT_SYMBOL_LOCAL 
) { 
1742                 handler(sectInfo
.sectAddr
+i
*elementSize
, false, 0, "", false, false, false, stop
); 
1745             if ( symNum 
> symCount 
) { 
1746                 diag
.error("indirect symbol[%d] = %d which is invalid symbol index", sectInfo
.reserved1 
+ i
, symNum
); 
1750             uint16_t n_desc 
= is64Bit 
? symbols64
[symNum
].n_desc 
: symbols32
[symNum
].n_desc
; 
1751             uint8_t  n_type     
= is64Bit 
? symbols64
[symNum
].n_type 
: symbols32
[symNum
].n_type
; 
1752             uint32_t libOrdinal 
= libOrdinalFromDesc(n_desc
); 
1753             uint32_t strOffset 
= is64Bit 
? symbols64
[symNum
].n_un
.n_strx 
: symbols32
[symNum
].n_un
.n_strx
; 
1754             if ( strOffset 
> poolSize 
) { 
1755                diag
.error("symbol[%d] string offset out of range", sectInfo
.reserved1 
+ i
); 
1759             const char* symbolName  
= stringPool 
+ strOffset
; 
1760             bool        weakImport  
= (n_desc 
& N_WEAK_REF
); 
1761             bool        lazy        
= (sectionType 
== S_LAZY_SYMBOL_POINTERS
); 
1762             // Handle defined weak def symbols which need to get a special ordinal 
1763             if ( ((n_type 
& N_TYPE
) == N_SECT
) && ((n_type 
& N_EXT
) != 0) && ((n_desc 
& N_WEAK_DEF
) != 0) ) 
1764                 libOrdinal 
= BIND_SPECIAL_DYLIB_WEAK_LOOKUP
; 
1765             handler(sectInfo
.sectAddr
+i
*elementSize
, true, libOrdinal
, symbolName
, weakImport
, lazy
, selfModifyingStub
, stop
); 
1771 int MachOAnalyzer::libOrdinalFromDesc(uint16_t n_desc
) const 
1773     // -flat_namespace is always flat lookup 
1774     if ( (this->flags 
& MH_TWOLEVEL
) == 0 ) 
1775         return BIND_SPECIAL_DYLIB_FLAT_LOOKUP
; 
1777     // extract byte from undefined symbol entry 
1778     int libIndex 
= GET_LIBRARY_ORDINAL(n_desc
); 
1779     switch ( libIndex 
) { 
1780         case SELF_LIBRARY_ORDINAL
: 
1781             return BIND_SPECIAL_DYLIB_SELF
; 
1783         case DYNAMIC_LOOKUP_ORDINAL
: 
1784             return BIND_SPECIAL_DYLIB_FLAT_LOOKUP
; 
1786         case EXECUTABLE_ORDINAL
: 
1787             return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
; 
1793 bool MachOAnalyzer::validBindInfo(Diagnostics
& diag
, const char* path
) const 
1795     forEachBind(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1796                          bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, 
1797                          uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, 
1798                          uint8_t type
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) { 
1799         if ( invalidBindState(diag
, opcodeName
, path
, leInfo
, segments
, segIndexSet
, libraryOrdinalSet
, dylibCount
, 
1800                               libOrdinal
, ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
) ) { 
1803     }, ^(const char* symbolName
) { 
1805     return diag
.noError(); 
1808 bool MachOAnalyzer::invalidBindState(Diagnostics
& diag
, const char* opcodeName
, const char* path
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1809                                     bool segIndexSet
,  bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, uint32_t ptrSize
, 
1810                                     uint8_t segmentIndex
, uint64_t segmentOffset
, uint8_t type
, const char* symbolName
) const 
1812     if ( !segIndexSet 
) { 
1813         diag
.error("in '%s' %s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path
, opcodeName
); 
1816     if ( segmentIndex 
>= leInfo
.layout
.linkeditSegIndex 
)  { 
1817         diag
.error("in '%s' %s segment index %d too large", path
, opcodeName
, segmentIndex
); 
1820     if ( segmentOffset 
> (segments
[segmentIndex
].vmSize
-ptrSize
) ) { 
1821         diag
.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path
, opcodeName
, segmentOffset
, segments
[segmentIndex
].vmSize
); 
1824     if ( symbolName 
== NULL 
) { 
1825         diag
.error("in '%s' %s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path
, opcodeName
); 
1828     if ( !libraryOrdinalSet 
) { 
1829         diag
.error("in '%s' %s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", path
, opcodeName
); 
1832     if ( libOrdinal 
> (int)dylibCount 
) { 
1833         diag
.error("in '%s' %s has library ordinal too large (%d) max (%d)", path
, opcodeName
, libOrdinal
, dylibCount
); 
1836     if ( libOrdinal 
< BIND_SPECIAL_DYLIB_WEAK_LOOKUP 
) { 
1837         diag
.error("in '%s' %s has unknown library special ordinal (%d)", path
, opcodeName
, libOrdinal
); 
1841         case BIND_TYPE_POINTER
: 
1842             if ( !segments
[segmentIndex
].writable() ) { 
1843                 diag
.error("in '%s' %s pointer bind is in non-writable segment", path
, opcodeName
); 
1846             if ( segments
[segmentIndex
].executable() && enforceFormat(Malformed::executableData
) ) { 
1847                 diag
.error("in '%s' %s pointer bind is in executable segment", path
, opcodeName
); 
1851         case BIND_TYPE_TEXT_ABSOLUTE32
: 
1852         case BIND_TYPE_TEXT_PCREL32
: { 
1853             // Text relocations are permitted in x86_64 kexts 
1854             bool forceAllowTextRelocs 
= false; 
1855 #if BUILDING_APP_CACHE_UTIL 
1856             if ( isKextBundle() && (isArch("x86_64") || isArch("x86_64h")) ) 
1857                 forceAllowTextRelocs 
= true; 
1859             if ( !forceAllowTextRelocs 
&& !segments
[segmentIndex
].textRelocs 
) { 
1860                 diag
.error("in '%s' %s text bind is in segment that does not support text relocations", path
, opcodeName
); 
1863             if ( segments
[segmentIndex
].writable() ) { 
1864                 diag
.error("in '%s' %s text bind is in writable segment", path
, opcodeName
); 
1867             if ( !segments
[segmentIndex
].executable() ) { 
1868                 diag
.error("in '%s' %s pointer bind is in non-executable segment", path
, opcodeName
); 
1874             diag
.error("in '%s' %s unknown bind type %d", path
, opcodeName
, type
); 
1880 void MachOAnalyzer::forEachBind(Diagnostics
& diag
, void (^handler
)(uint64_t runtimeOffset
, int libOrdinal
, uint8_t type
, const char* symbolName
, 
1881                                                                   bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
), 
1882                                                    void (^strongHandler
)(const char* symbolName
)) const 
1884     __block 
bool     startVmAddrSet 
= false; 
1885     __block 
uint64_t startVmAddr    
= 0; 
1886     forEachBind(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1887                         bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, 
1888                         uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, 
1889                         uint8_t type
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) { 
1890        if ( !startVmAddrSet 
) { 
1891             for (int i
=0; i 
<= segmentIndex
; ++i
) { 
1892                 if ( strcmp(segments
[i
].segName
, "__TEXT") == 0 ) { 
1893                     startVmAddr 
= segments
[i
].vmAddr
; 
1894                     startVmAddrSet 
= true; 
1899         uint64_t bindVmOffset  
= segments
[segmentIndex
].vmAddr 
+ segmentOffset
; 
1900         uint64_t runtimeOffset 
= bindVmOffset 
- startVmAddr
; 
1901         handler(runtimeOffset
, libOrdinal
, type
, symbolName
, weakImport
, lazyBind
, addend
, stop
); 
1902     }, ^(const char* symbolName
) { 
1903         strongHandler(symbolName
); 
1907 void MachOAnalyzer::forEachBind(Diagnostics
& diag
, void (^handler
)(uint64_t runtimeOffset
, int libOrdinal
, const char* symbolName
, 
1908                                                                   bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
), 
1909                                                    void (^strongHandler
)(const char* symbolName
)) const 
1911     forEachBind(diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, uint8_t type
, const char* symbolName
, 
1912                         bool weakImport
, bool lazyBind
, uint64_t addend
, bool &stop
) { 
1913         handler(runtimeOffset
, libOrdinal
, symbolName
, weakImport
, lazyBind
, addend
, stop
); 
1917 void MachOAnalyzer::forEachBind(Diagnostics
& diag
, 
1918                                  void (^handler
)(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], 
1919                                                  bool segIndexSet
,  bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, 
1920                                                  uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, uint8_t type
, 
1921                                                  const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
), 
1922                                  void (^strongHandler
)(const char* symbolName
)) const 
1924     const uint32_t  ptrSize 
= this->pointerSize(); 
1927     LinkEditInfo leInfo
; 
1928     getLinkEditPointers(diag
, leInfo
); 
1929     if ( diag
.hasError() ) 
1932     BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1); 
1933     getAllSegmentsInfos(diag
, segmentsInfo
); 
1934     if ( diag
.hasError() ) 
1939     const uint32_t dylibCount 
= dependentDylibCount(); 
1941     if ( leInfo
.dyldInfo 
!= nullptr ) { 
1942         // process bind opcodes 
1943         const uint8_t*  p    
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->bind_off
); 
1944         const uint8_t*  end  
= p 
+ leInfo
.dyldInfo
->bind_size
; 
1946         uint64_t        segmentOffset 
= 0; 
1947         uint8_t         segmentIndex 
= 0; 
1948         const char*     symbolName 
= NULL
; 
1949         int             libraryOrdinal 
= 0; 
1950         bool            segIndexSet 
= false; 
1951         bool            libraryOrdinalSet 
= false; 
1956         bool            weakImport 
= false; 
1957         while ( !stop 
&& diag
.noError() && (p 
< end
) ) { 
1958             uint8_t immediate 
= *p 
& BIND_IMMEDIATE_MASK
; 
1959             uint8_t opcode 
= *p 
& BIND_OPCODE_MASK
; 
1962                 case BIND_OPCODE_DONE
: 
1965                 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
: 
1966                     libraryOrdinal 
= immediate
; 
1967                     libraryOrdinalSet 
= true; 
1969                 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
: 
1970                     libraryOrdinal 
= (int)read_uleb128(diag
, p
, end
); 
1971                     libraryOrdinalSet 
= true; 
1973                 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
: 
1974                     // the special ordinals are negative numbers 
1975                     if ( immediate 
== 0 ) 
1978                         int8_t signExtended 
= BIND_OPCODE_MASK 
| immediate
; 
1979                         libraryOrdinal 
= signExtended
; 
1981                     libraryOrdinalSet 
= true; 
1983                 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
: 
1984                     weakImport 
= ( (immediate 
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 ); 
1985                     symbolName 
= (char*)p
; 
1990                 case BIND_OPCODE_SET_TYPE_IMM
: 
1993                 case BIND_OPCODE_SET_ADDEND_SLEB
: 
1994                     addend 
= read_sleb128(diag
, p
, end
); 
1996                 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
: 
1997                     segmentIndex 
= immediate
; 
1998                     segmentOffset 
= read_uleb128(diag
, p
, end
); 
2001                 case BIND_OPCODE_ADD_ADDR_ULEB
: 
2002                     segmentOffset 
+= read_uleb128(diag
, p
, end
); 
2004                 case BIND_OPCODE_DO_BIND
: 
2005                     handler("BIND_OPCODE_DO_BIND", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2006                             ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2007                     segmentOffset 
+= ptrSize
; 
2009                 case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
: 
2010                     handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2011                             ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2012                     segmentOffset 
+= read_uleb128(diag
, p
, end
) + ptrSize
; 
2014                 case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
: 
2015                     handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2016                             ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2017                     segmentOffset 
+= immediate
*ptrSize 
+ ptrSize
; 
2019                 case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
: 
2020                     count 
= read_uleb128(diag
, p
, end
); 
2021                     skip 
= read_uleb128(diag
, p
, end
); 
2022                     for (uint32_t i
=0; i 
< count
; ++i
) { 
2023                         handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2024                                 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2025                         segmentOffset 
+= skip 
+ ptrSize
; 
2031                     diag
.error("bad bind opcode 0x%02X", *p
); 
2034         if ( diag
.hasError() ) 
2037         // process lazy bind opcodes 
2038         uint32_t lazyDoneCount 
= 0; 
2039         uint32_t lazyBindCount 
= 0; 
2040         if ( leInfo
.dyldInfo
->lazy_bind_size 
!= 0 ) { 
2041             p               
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->lazy_bind_off
); 
2042             end             
= p 
+ leInfo
.dyldInfo
->lazy_bind_size
; 
2043             type            
= BIND_TYPE_POINTER
; 
2048             segIndexSet     
= false; 
2049             libraryOrdinalSet
= false; 
2053             while (  !stop 
&& diag
.noError() && (p 
< end
) ) { 
2054                 uint8_t immediate 
= *p 
& BIND_IMMEDIATE_MASK
; 
2055                 uint8_t opcode 
= *p 
& BIND_OPCODE_MASK
; 
2058                     case BIND_OPCODE_DONE
: 
2059                         // this opcode marks the end of each lazy pointer binding 
2062                     case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
: 
2063                         libraryOrdinal 
= immediate
; 
2064                         libraryOrdinalSet 
= true; 
2066                     case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
: 
2067                         libraryOrdinal 
= (int)read_uleb128(diag
, p
, end
); 
2068                         libraryOrdinalSet 
= true; 
2070                     case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
: 
2071                         // the special ordinals are negative numbers 
2072                         if ( immediate 
== 0 ) 
2075                             int8_t signExtended 
= BIND_OPCODE_MASK 
| immediate
; 
2076                             libraryOrdinal 
= signExtended
; 
2078                         libraryOrdinalSet 
= true; 
2080                     case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
: 
2081                         weakImport 
= ( (immediate 
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 ); 
2082                         symbolName 
= (char*)p
; 
2087                     case BIND_OPCODE_SET_ADDEND_SLEB
: 
2088                         addend 
= read_sleb128(diag
, p
, end
); 
2090                     case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
: 
2091                         segmentIndex 
= immediate
; 
2092                         segmentOffset 
= read_uleb128(diag
, p
, end
); 
2095                     case BIND_OPCODE_DO_BIND
: 
2096                         handler("BIND_OPCODE_DO_BIND", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2097                                 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, true, addend
, stop
); 
2098                         segmentOffset 
+= ptrSize
; 
2101                     case BIND_OPCODE_SET_TYPE_IMM
: 
2102                     case BIND_OPCODE_ADD_ADDR_ULEB
: 
2103                     case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
: 
2104                     case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
: 
2105                     case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
: 
2107                         diag
.error("bad lazy bind opcode 0x%02X", opcode
); 
2111             if ( lazyDoneCount 
> lazyBindCount
+7 ) { 
2112                 // diag.error("lazy bind opcodes missing binds"); 
2115         if ( diag
.hasError() ) 
2118         // process weak bind info 
2119         if ( leInfo
.dyldInfo
->weak_bind_size 
!= 0 ) { 
2120             p               
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->weak_bind_off
); 
2121             end             
= p 
+ leInfo
.dyldInfo
->weak_bind_size
; 
2122             type            
= BIND_TYPE_POINTER
; 
2126             libraryOrdinal  
= BIND_SPECIAL_DYLIB_WEAK_LOOKUP
; 
2127             segIndexSet     
= false; 
2128             libraryOrdinalSet
= true; 
2132             while ( !stop 
&& diag
.noError() && (p 
< end
) ) { 
2133                 uint8_t immediate 
= *p 
& BIND_IMMEDIATE_MASK
; 
2134                 uint8_t opcode 
= *p 
& BIND_OPCODE_MASK
; 
2137                     case BIND_OPCODE_DONE
: 
2140                     case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
: 
2141                     case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
: 
2142                     case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
: 
2143                         diag
.error("unexpected dylib ordinal in weak_bind"); 
2145                     case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
: 
2146                         weakImport 
= ( (immediate 
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 ); 
2147                         symbolName 
= (char*)p
; 
2151                         if ( immediate 
& BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION 
) { 
2152                             strongHandler(symbolName
); 
2155                     case BIND_OPCODE_SET_TYPE_IMM
: 
2158                     case BIND_OPCODE_SET_ADDEND_SLEB
: 
2159                         addend 
= read_sleb128(diag
, p
, end
); 
2161                     case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
: 
2162                         segmentIndex 
= immediate
; 
2163                         segmentOffset 
= read_uleb128(diag
, p
, end
); 
2166                     case BIND_OPCODE_ADD_ADDR_ULEB
: 
2167                         segmentOffset 
+= read_uleb128(diag
, p
, end
); 
2169                     case BIND_OPCODE_DO_BIND
: 
2170                         handler("BIND_OPCODE_DO_BIND", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2171                                 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2172                         segmentOffset 
+= ptrSize
; 
2174                     case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB
: 
2175                         handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2176                                 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2177                         segmentOffset 
+= read_uleb128(diag
, p
, end
) + ptrSize
; 
2179                     case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED
: 
2180                         handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2181                                 ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2182                         segmentOffset 
+= immediate
*ptrSize 
+ ptrSize
; 
2184                     case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB
: 
2185                         count 
= read_uleb128(diag
, p
, end
); 
2186                         skip 
= read_uleb128(diag
, p
, end
); 
2187                         for (uint32_t i
=0; i 
< count
; ++i
) { 
2188                             handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo
, segmentsInfo
, segIndexSet
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, 
2189                                     ptrSize
, segmentIndex
, segmentOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2190                             segmentOffset 
+= skip 
+ ptrSize
; 
2196                         diag
.error("bad bind opcode 0x%02X", *p
); 
2201     else if ( leInfo
.chainedFixups 
!= nullptr ) { 
2202         // binary uses chained fixups, so do nothing 
2204     else if ( leInfo
.dynSymTab 
!= nullptr ) { 
2205         // old binary, process external relocations 
2206         const uint64_t                  relocsStartAddress 
= externalRelocBaseAddress(segmentsInfo
, leInfo
.layout
.linkeditSegIndex
); 
2207         const relocation_info
* const    relocsStart 
= (relocation_info
*)getLinkEditContent(leInfo
.layout
, leInfo
.dynSymTab
->extreloff
); 
2208         const relocation_info
* const    relocsEnd   
= &relocsStart
[leInfo
.dynSymTab
->nextrel
]; 
2209         bool                            is64Bit     
= is64() ; 
2210         const uint8_t                   relocSize   
= (is64Bit 
? 3 : 2); 
2211         const void*                     symbolTable 
= getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->symoff
); 
2212         const struct nlist_64
*          symbols64   
= (nlist_64
*)symbolTable
; 
2213         const struct nlist
*             symbols32   
= (struct nlist
*)symbolTable
; 
2214         const char*                     stringPool  
= (char*)getLinkEditContent(leInfo
.layout
, leInfo
.symTab
->stroff
); 
2215         uint32_t                        symCount    
= leInfo
.symTab
->nsyms
; 
2216         uint32_t                        poolSize    
= leInfo
.symTab
->strsize
; 
2217         for (const relocation_info
* reloc
=relocsStart
; (reloc 
< relocsEnd
) && !stop
; ++reloc
) { 
2218             bool isBranch 
= false; 
2219 #if BUILDING_APP_CACHE_UTIL 
2220             if ( isKextBundle() ) { 
2221                 // kext's may have other kinds of relocations, eg, branch relocs.  Skip them 
2222                 if ( isArch("x86_64") || isArch("x86_64h") ) { 
2223                     if ( reloc
->r_type 
== X86_64_RELOC_BRANCH 
) { 
2224                         if ( reloc
->r_length 
!= 2 ) { 
2225                             diag
.error("external relocation has wrong r_length"); 
2228                         if ( reloc
->r_pcrel 
!= true ) { 
2229                             diag
.error("external relocation should be pcrel"); 
2239                 if ( reloc
->r_length 
!= relocSize 
) { 
2240                     diag
.error("external relocation has wrong r_length"); 
2243                 if ( reloc
->r_type 
!= 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED 
2244                     diag
.error("external relocation has wrong r_type"); 
2248             uint32_t segIndex  
= 0; 
2249             uint64_t segOffset 
= 0; 
2250             if ( segIndexAndOffsetForAddress(relocsStartAddress
+reloc
->r_address
, segmentsInfo
, leInfo
.layout
.linkeditSegIndex
, segIndex
, segOffset
) ) { 
2251                 uint32_t symbolIndex 
= reloc
->r_symbolnum
; 
2252                 if ( symbolIndex 
> symCount 
) { 
2253                     diag
.error("external relocation has out of range r_symbolnum"); 
2257                     uint32_t strOffset  
= is64Bit 
? symbols64
[symbolIndex
].n_un
.n_strx 
: symbols32
[symbolIndex
].n_un
.n_strx
; 
2258                     uint16_t n_desc     
= is64Bit 
? symbols64
[symbolIndex
].n_desc 
: symbols32
[symbolIndex
].n_desc
; 
2259                     uint8_t  n_type     
= is64Bit 
? symbols64
[symbolIndex
].n_type 
: symbols32
[symbolIndex
].n_type
; 
2260                     uint32_t libOrdinal 
= libOrdinalFromDesc(n_desc
); 
2261                     if ( strOffset 
>= poolSize 
) { 
2262                         diag
.error("external relocation has r_symbolnum=%d which has out of range n_strx", symbolIndex
); 
2266                         const char*     symbolName 
= stringPool 
+ strOffset
; 
2267                         bool            weakImport 
= (n_desc 
& N_WEAK_REF
); 
2268                         const uint8_t*  content    
= (uint8_t*)this + segmentsInfo
[segIndex
].vmAddr 
- leInfo
.layout
.textUnslidVMAddr 
+ segOffset
; 
2269                         uint64_t        addend     
= (reloc
->r_length 
== 3) ? *((uint64_t*)content
) : *((uint32_t*)content
); 
2270                         // Handle defined weak def symbols which need to get a special ordinal 
2271                         if ( ((n_type 
& N_TYPE
) == N_SECT
) && ((n_type 
& N_EXT
) != 0) && ((n_desc 
& N_WEAK_DEF
) != 0) ) 
2272                             libOrdinal 
= BIND_SPECIAL_DYLIB_WEAK_LOOKUP
; 
2273                         uint8_t type 
= isBranch 
? BIND_TYPE_TEXT_PCREL32 
: BIND_TYPE_POINTER
; 
2274                         handler("external relocation", leInfo
, segmentsInfo
, true, true, dylibCount
, libOrdinal
, 
2275                                 ptrSize
, segIndex
, segOffset
, type
, symbolName
, weakImport
, false, addend
, stop
); 
2280                 diag
.error("local relocation has out of range r_address"); 
2284         // then process indirect symbols 
2285         forEachIndirectPointer(diag
, ^(uint64_t address
, bool bind
, int bindLibOrdinal
, 
2286                                        const char* bindSymbolName
, bool bindWeakImport
, bool bindLazy
, bool selfModifyingStub
, bool& indStop
) { 
2289             uint32_t segIndex  
= 0; 
2290             uint64_t segOffset 
= 0; 
2291             if ( segIndexAndOffsetForAddress(address
, segmentsInfo
, leInfo
.layout
.linkeditSegIndex
, segIndex
, segOffset
) ) { 
2292                 handler("indirect symbol", leInfo
, segmentsInfo
, true, true, dylibCount
, bindLibOrdinal
, 
2293                          ptrSize
, segIndex
, segOffset
, BIND_TYPE_POINTER
, bindSymbolName
, bindWeakImport
, bindLazy
, 0, indStop
); 
2296                 diag
.error("indirect symbol has out of range address"); 
2304 bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics
& diag
, const char* path
) const 
2306     LinkEditInfo leInfo
; 
2307     getLinkEditPointers(diag
, leInfo
); 
2308     if ( diag
.hasError() ) 
2311     BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1); 
2312     getAllSegmentsInfos(diag
, segmentsInfo
); 
2313     if ( diag
.hasError() ) 
2316     // validate dyld_chained_fixups_header 
2317     const dyld_chained_fixups_header
* chainsHeader 
= (dyld_chained_fixups_header
*)getLinkEditContent(leInfo
.layout
, leInfo
.chainedFixups
->dataoff
); 
2318     if ( chainsHeader
->fixups_version 
!= 0 ) { 
2319         diag
.error("chained fixups, unknown header version"); 
2322     if ( chainsHeader
->starts_offset 
>= leInfo
.chainedFixups
->datasize 
)  { 
2323         diag
.error("chained fixups, starts_offset exceeds LC_DYLD_CHAINED_FIXUPS size"); 
2326     if ( chainsHeader
->imports_offset 
> leInfo
.chainedFixups
->datasize 
)  { 
2327         diag
.error("chained fixups, imports_offset exceeds LC_DYLD_CHAINED_FIXUPS size"); 
2331     uint32_t formatEntrySize
; 
2332     switch ( chainsHeader
->imports_format 
) { 
2333         case DYLD_CHAINED_IMPORT
: 
2334             formatEntrySize 
= sizeof(dyld_chained_import
); 
2336         case DYLD_CHAINED_IMPORT_ADDEND
: 
2337             formatEntrySize 
= sizeof(dyld_chained_import_addend
); 
2339         case DYLD_CHAINED_IMPORT_ADDEND64
: 
2340             formatEntrySize 
= sizeof(dyld_chained_import_addend64
); 
2343             diag
.error("chained fixups, unknown imports_format"); 
2346     if ( greaterThanAddOrOverflow(chainsHeader
->imports_offset
, (formatEntrySize 
* chainsHeader
->imports_count
), chainsHeader
->symbols_offset
) ) { 
2347          diag
.error("chained fixups, imports array overlaps symbols"); 
2350     if ( chainsHeader
->symbols_format 
!= 0 )  { 
2351          diag
.error("chained fixups, symbols_format unknown"); 
2355     // validate dyld_chained_starts_in_image 
2356     const dyld_chained_starts_in_image
* startsInfo 
= (dyld_chained_starts_in_image
*)((uint8_t*)chainsHeader 
+ chainsHeader
->starts_offset
); 
2357     if ( startsInfo
->seg_count 
!= leInfo
.layout
.linkeditSegIndex
+1 ) { 
2358         // We can have fewer segments than the count, so long as those we are missing have no relocs 
2359         // This can happen because __CTF is inserted by ctf_insert after linking, and between __DATA and __LINKEDIT, but has no relocs 
2360         // ctf_insert updates the load commands to put __CTF between __DATA and __LINKEDIT, but doesn't update the chained fixups data structures 
2361         if ( startsInfo
->seg_count 
> (leInfo
.layout
.linkeditSegIndex 
+ 1) ) { 
2362             diag
.error("chained fixups, seg_count exceeds number of segments"); 
2366         // We can have fewer segments than the count, so long as those we are missing have no relocs 
2367         uint32_t numNoRelocSegments 
= 0; 
2368         uint32_t numExtraSegments 
= (leInfo
.layout
.lastSegIndex 
+ 1) - startsInfo
->seg_count
; 
2369         for (unsigned i 
= 0; i 
!= numExtraSegments
; ++i
) { 
2370             // Check each extra segment before linkedit 
2371             const SegmentInfo
& segInfo 
= segmentsInfo
[leInfo
.layout
.linkeditSegIndex 
- (i 
+ 1)]; 
2372             if ( segInfo
.vmSize 
== 0 ) 
2373                 ++numNoRelocSegments
; 
2376         if ( numNoRelocSegments 
!= numExtraSegments 
) { 
2377             diag
.error("chained fixups, seg_count does not match number of segments"); 
2381     const uint64_t baseAddress 
= preferredLoadAddress(); 
2382     uint32_t maxValidPointerSeen 
= 0; 
2383     uint16_t pointer_format_for_all 
= 0; 
2384     bool pointer_format_found 
= false; 
2385     const uint8_t* endOfStarts 
= (uint8_t*)chainsHeader 
+ chainsHeader
->imports_offset
; 
2386     for (uint32_t i
=0; i 
< startsInfo
->seg_count
; ++i
) { 
2387         uint32_t segInfoOffset 
= startsInfo
->seg_info_offset
[i
]; 
2388         // 0 offset means this segment has no fixups 
2389         if ( segInfoOffset 
== 0 ) 
2391         const dyld_chained_starts_in_segment
* segInfo 
= (dyld_chained_starts_in_segment
*)((uint8_t*)startsInfo 
+ segInfoOffset
); 
2392         if ( segInfo
->size 
> (endOfStarts 
- (uint8_t*)segInfo
) ) { 
2393              diag
.error("chained fixups, dyld_chained_starts_in_segment for segment #%d overruns imports table", i
); 
2397         // validate dyld_chained_starts_in_segment 
2398         if ( (segInfo
->page_size 
!= 0x1000) && (segInfo
->page_size 
!= 0x4000) ) { 
2399             diag
.error("chained fixups, page_size not 4KB or 16KB in segment #%d", i
); 
2402         if ( segInfo
->pointer_format 
> 12 ) { 
2403             diag
.error("chained fixups, unknown pointer_format in segment #%d", i
); 
2406         if ( !pointer_format_found 
) { 
2407             pointer_format_for_all 
= segInfo
->pointer_format
; 
2408             pointer_format_found 
= true; 
2410         if ( segInfo
->pointer_format 
!= pointer_format_for_all
) { 
2411             diag
.error("chained fixups, pointer_format not same for all segments %d and %d", segInfo
->pointer_format
, pointer_format_for_all
); 
2414         if ( segInfo
->segment_offset 
!= (segmentsInfo
[i
].vmAddr 
- baseAddress
) ) { 
2415             diag
.error("chained fixups, segment_offset does not match vmaddr from LC_SEGMENT in segment #%d", i
); 
2418         if ( segInfo
->max_valid_pointer 
!= 0 ) { 
2419             if ( maxValidPointerSeen 
== 0 ) { 
2420                 // record max_valid_pointer values seen 
2421                 maxValidPointerSeen 
= segInfo
->max_valid_pointer
; 
2423             else if ( maxValidPointerSeen 
!= segInfo
->max_valid_pointer 
) { 
2424                 diag
.error("chained fixups, different max_valid_pointer values seen in different segments"); 
2428         // validate starts table in segment 
2429         if ( offsetof(dyld_chained_starts_in_segment
, page_start
[segInfo
->page_count
]) > segInfo
->size 
) { 
2430             diag
.error("chained fixups, page_start array overflows size"); 
2433         uint32_t maxOverflowIndex 
= (uint32_t)(segInfo
->size 
- offsetof(dyld_chained_starts_in_segment
, page_start
[segInfo
->page_count
]))/sizeof(uint16_t); 
2434         for (int pageIndex
=0; pageIndex 
< segInfo
->page_count
; ++pageIndex
) { 
2435             uint16_t offsetInPage 
= segInfo
->page_start
[pageIndex
]; 
2436             if ( offsetInPage 
== DYLD_CHAINED_PTR_START_NONE 
) 
2438             if ( (offsetInPage 
& DYLD_CHAINED_PTR_START_MULTI
) == 0 ) { 
2439                 // this is the offset into the page where the first fixup is 
2440                 if ( offsetInPage 
> segInfo
->page_size 
) { 
2441                     diag
.error("chained fixups, in segment #%d page_start[%d]=0x%04X exceeds page size", i
, pageIndex
, offsetInPage
); 
2445                 // this is actually an index into chain_starts[] 
2446                 uint32_t overflowIndex 
= offsetInPage 
& ~DYLD_CHAINED_PTR_START_MULTI
; 
2447                 // now verify all starts are within the page and in ascending order 
2448                 uint16_t lastOffsetInPage 
= 0; 
2450                     if ( overflowIndex 
> maxOverflowIndex 
)  { 
2451                         diag
.error("chain overflow index out of range %d (max=%d) in segment %s", overflowIndex
, maxOverflowIndex
, segmentName(i
)); 
2454                     offsetInPage 
= (segInfo
->page_start
[overflowIndex
] & ~DYLD_CHAINED_PTR_START_LAST
); 
2455                     if ( offsetInPage 
> segInfo
->page_size 
) { 
2456                         diag
.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X exceeds page size", i
, overflowIndex
, offsetInPage
); 
2459                     if ( (offsetInPage 
<= lastOffsetInPage
) && (lastOffsetInPage 
!= 0) )  { 
2460                         diag
.error("chained fixups, in segment #%d overflow page_start[%d]=0x%04X is before previous at 0x%04X\n", i
, overflowIndex
, offsetInPage
, lastOffsetInPage
); 
2463                     lastOffsetInPage 
= offsetInPage
; 
2465                 } while ( (segInfo
->page_start
[overflowIndex
] & DYLD_CHAINED_PTR_START_LAST
) == 0 ); 
2470     // validate import table size can fit 
2471     if ( chainsHeader
->imports_count 
!= 0 ) { 
2472         uint32_t maxBindOrdinal 
= 0; 
2473         switch (pointer_format_for_all
) { 
2474             case DYLD_CHAINED_PTR_32
: 
2475                 maxBindOrdinal 
= 0x0FFFFF; // 20-bits 
2477             case DYLD_CHAINED_PTR_ARM64E
: 
2478             case DYLD_CHAINED_PTR_ARM64E_USERLAND
: 
2479             case DYLD_CHAINED_PTR_ARM64E_OFFSET
: 
2480                 maxBindOrdinal 
= 0x00FFFF; // 16-bits 
2482             case DYLD_CHAINED_PTR_64
: 
2483             case DYLD_CHAINED_PTR_64_OFFSET
: 
2484             case DYLD_CHAINED_PTR_ARM64E_USERLAND24
: 
2485                 maxBindOrdinal 
= 0xFFFFFF; // 24 bits 
2488         if ( chainsHeader
->imports_count 
>= maxBindOrdinal 
)  { 
2489             diag
.error("chained fixups, imports_count (%d) exceeds max of %d", chainsHeader
->imports_count
, maxBindOrdinal
); 
2494     // validate max_valid_pointer is larger than last segment 
2495     if ( (maxValidPointerSeen 
!= 0) && !inDyldCache() ) { 
2496         uint64_t lastSegmentLastVMAddr 
= segmentsInfo
[leInfo
.layout
.linkeditSegIndex
-1].vmAddr 
+ segmentsInfo
[leInfo
.layout
.linkeditSegIndex
-1].vmSize
; 
2497         if ( maxValidPointerSeen 
< lastSegmentLastVMAddr 
) { 
2498             diag
.error("chained fixups, max_valid_pointer too small for image"); 
2503     return diag
.noError(); 
2506 bool MachOAnalyzer::validChainedFixupsInfoOldArm64e(Diagnostics
& diag
, const char* path
) const 
2508     __block 
uint32_t maxTargetCount 
= 0; 
2509     __block 
uint32_t currentTargetCount 
= 0; 
2510     parseOrgArm64eChainedFixups(diag
, 
2511         ^(uint32_t totalTargets
, bool& stop
) { 
2512             maxTargetCount 
= totalTargets
; 
2514         ^(const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, uint8_t type
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) { 
2515            if ( symbolName 
== NULL 
) { 
2516                 diag
.error("in '%s' missing BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path
); 
2518             else if ( !libraryOrdinalSet 
) { 
2519                 diag
.error("in '%s' missing BIND_OPCODE_SET_DYLIB_ORDINAL", path
); 
2521             else if ( libOrdinal 
> (int)dylibCount 
) { 
2522                 diag
.error("in '%s' has library ordinal too large (%d) max (%d)", path
, libOrdinal
, dylibCount
); 
2524             else if ( libOrdinal 
< BIND_SPECIAL_DYLIB_WEAK_LOOKUP 
) { 
2525                 diag
.error("in '%s' has unknown library special ordinal (%d)", path
, libOrdinal
); 
2527             else if ( type 
!= BIND_TYPE_POINTER 
) { 
2528                 diag
.error("in '%s' unknown bind type %d", path
, type
); 
2530             else if ( currentTargetCount 
> maxTargetCount 
) { 
2531                 diag
.error("in '%s' chained target counts exceeds BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB", path
); 
2533             ++currentTargetCount
; 
2534             if ( diag
.hasError() ) 
2537         ^(const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], uint8_t segmentIndex
, bool segIndexSet
, uint64_t segmentOffset
, uint16_t format
, bool& stop
) { 
2538            if ( !segIndexSet 
) { 
2539                 diag
.error("in '%s' missing BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path
); 
2541             else if ( segmentIndex 
>= leInfo
.layout
.linkeditSegIndex 
)  { 
2542                 diag
.error("in '%s' segment index %d too large", path
, segmentIndex
); 
2544             else if ( segmentOffset 
> (segments
[segmentIndex
].vmSize
-8) ) { 
2545                 diag
.error("in '%s' current segment offset 0x%08llX beyond segment size (0x%08llX)", path
, segmentOffset
, segments
[segmentIndex
].vmSize
); 
2547             else if ( !segments
[segmentIndex
].writable() ) { 
2548                 diag
.error("in '%s' pointer bind is in non-writable segment", path
); 
2550             else if ( segments
[segmentIndex
].executable() ) { 
2551                 diag
.error("in '%s' pointer bind is in executable segment", path
); 
2553             if ( diag
.hasError() ) 
2558     return diag
.noError(); 
2563 void MachOAnalyzer::parseOrgArm64eChainedFixups(Diagnostics
& diag
, void (^targetCount
)(uint32_t totalTargets
, bool& stop
), 
2564                                                                    void (^addTarget
)(const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, uint8_t type
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
), 
2565                                                                    void (^addChainStart
)(const LinkEditInfo
& leInfo
, const SegmentInfo segments
[], uint8_t segmentIndex
, bool segIndexSet
, uint64_t segmentOffset
, uint16_t format
, bool& stop
)) const 
2569     LinkEditInfo leInfo
; 
2570     getLinkEditPointers(diag
, leInfo
); 
2571     if ( diag
.hasError() ) 
2574     BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1); 
2575     getAllSegmentsInfos(diag
, segmentsInfo
); 
2576     if ( diag
.hasError() ) 
2579     const uint32_t dylibCount 
= dependentDylibCount(); 
2581     if ( leInfo
.dyldInfo 
!= nullptr ) { 
2582         // process bind opcodes 
2583         const uint8_t*  p    
= getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->bind_off
); 
2584         const uint8_t*  end  
= p 
+ leInfo
.dyldInfo
->bind_size
; 
2586         uint64_t        segmentOffset 
= 0; 
2587         uint8_t         segmentIndex 
= 0; 
2588         const char*     symbolName 
= NULL
; 
2589         int             libraryOrdinal 
= 0; 
2590         bool            segIndexSet 
= false; 
2591         bool            libraryOrdinalSet 
= false; 
2592         uint64_t        targetTableCount
; 
2593         uint64_t        addend 
= 0; 
2594         bool            weakImport 
= false; 
2595         while ( !stop 
&& diag
.noError() && (p 
< end
) ) { 
2596             uint8_t immediate 
= *p 
& BIND_IMMEDIATE_MASK
; 
2597             uint8_t opcode 
= *p 
& BIND_OPCODE_MASK
; 
2600                 case BIND_OPCODE_DONE
: 
2603                 case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
: 
2604                     libraryOrdinal 
= immediate
; 
2605                     libraryOrdinalSet 
= true; 
2607                 case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB
: 
2608                     libraryOrdinal 
= (int)read_uleb128(diag
, p
, end
); 
2609                     libraryOrdinalSet 
= true; 
2611                 case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM
: 
2612                     // the special ordinals are negative numbers 
2613                     if ( immediate 
== 0 ) 
2616                         int8_t signExtended 
= BIND_OPCODE_MASK 
| immediate
; 
2617                         libraryOrdinal 
= signExtended
; 
2619                     libraryOrdinalSet 
= true; 
2621                 case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
: 
2622                     weakImport 
= ( (immediate 
& BIND_SYMBOL_FLAGS_WEAK_IMPORT
) != 0 ); 
2623                     symbolName 
= (char*)p
; 
2628                 case BIND_OPCODE_SET_TYPE_IMM
: 
2631                 case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
: 
2632                     segmentIndex 
= immediate
; 
2633                     segmentOffset 
= read_uleb128(diag
, p
, end
); 
2636                 case BIND_OPCODE_SET_ADDEND_SLEB
: 
2637                     addend 
= read_sleb128(diag
, p
, end
); 
2639                 case BIND_OPCODE_DO_BIND
: 
2641                         addTarget(leInfo
, segmentsInfo
, libraryOrdinalSet
, dylibCount
, libraryOrdinal
, type
, symbolName
, addend
, weakImport
, stop
); 
2643                 case BIND_OPCODE_THREADED
: 
2644                     switch (immediate
) { 
2645                         case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
: 
2646                             targetTableCount 
= read_uleb128(diag
, p
, end
); 
2647                             if ( targetTableCount 
> 65535 ) { 
2648                                 diag
.error("BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB size too large"); 
2653                                     targetCount((uint32_t)targetTableCount
, stop
); 
2656                         case BIND_SUBOPCODE_THREADED_APPLY
: 
2657                             if ( addChainStart 
) 
2658                                 addChainStart(leInfo
, segmentsInfo
, segmentIndex
, segIndexSet
, segmentOffset
, DYLD_CHAINED_PTR_ARM64E
, stop
); 
2661                             diag
.error("bad BIND_OPCODE_THREADED sub-opcode 0x%02X", immediate
); 
2665                     diag
.error("bad bind opcode 0x%02X", immediate
); 
2668         if ( diag
.hasError() ) 
2673 void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics
& diag
, void (^callback
)(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
)) const 
2675     LinkEditInfo leInfo
; 
2676     getLinkEditPointers(diag
, leInfo
); 
2677     if ( diag
.hasError() ) 
2680     BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo
, segmentsInfo
, leInfo
.layout
.lastSegIndex
+1); 
2681     getAllSegmentsInfos(diag
, segmentsInfo
); 
2682     if ( diag
.hasError() ) 
2686     if ( leInfo
.dyldInfo 
!= nullptr ) { 
2687         parseOrgArm64eChainedFixups(diag
, nullptr, ^(const LinkEditInfo
& leInfo2
, const SegmentInfo segments
[], bool libraryOrdinalSet
, uint32_t dylibCount
, 
2688                                                     int libOrdinal
, uint8_t type
, const char* symbolName
, uint64_t fixAddend
, bool weakImport
, bool& stopChain
) { 
2689             callback(libOrdinal
, symbolName
, fixAddend
, weakImport
, stopChain
); 
2692     else if ( leInfo
.chainedFixups 
!= nullptr ) { 
2693         const dyld_chained_fixups_header
*  header 
= (dyld_chained_fixups_header
*)getLinkEditContent(leInfo
.layout
, leInfo
.chainedFixups
->dataoff
); 
2694         if ( (header
->imports_offset 
> leInfo
.chainedFixups
->datasize
) || (header
->symbols_offset 
> leInfo
.chainedFixups
->datasize
) ) { 
2695             diag
.error("malformed import table"); 
2698         const dyld_chained_import
*          imports
; 
2699         const dyld_chained_import_addend
*   importsA32
; 
2700         const dyld_chained_import_addend64
* importsA64
; 
2701         const char*                         symbolsPool     
= (char*)header 
+ header
->symbols_offset
; 
2702         uint32_t                            maxSymbolOffset 
= leInfo
.chainedFixups
->datasize 
- header
->symbols_offset
; 
2704         switch (header
->imports_format
) { 
2705             case DYLD_CHAINED_IMPORT
: 
2706                 imports 
= (dyld_chained_import
*)((uint8_t*)header 
+ header
->imports_offset
); 
2707                 for (uint32_t i
=0; i 
< header
->imports_count 
&& !stop
; ++i
) { 
2708                     const char* symbolName 
= &symbolsPool
[imports
[i
].name_offset
]; 
2709                     if ( imports
[i
].name_offset 
> maxSymbolOffset 
) { 
2710                         diag
.error("malformed import table, string overflow"); 
2713                     uint8_t libVal 
= imports
[i
].lib_ordinal
; 
2714                     if ( libVal 
> 0xF0 ) 
2715                         libOrdinal 
= (int8_t)libVal
; 
2717                         libOrdinal 
= libVal
; 
2718                     callback(libOrdinal
, symbolName
, 0, imports
[i
].weak_import
, stop
); 
2721             case DYLD_CHAINED_IMPORT_ADDEND
: 
2722                 importsA32 
= (dyld_chained_import_addend
*)((uint8_t*)header 
+ header
->imports_offset
); 
2723                 for (uint32_t i
=0; i 
< header
->imports_count 
&& !stop
; ++i
) { 
2724                     const char* symbolName 
= &symbolsPool
[importsA32
[i
].name_offset
]; 
2725                     if ( importsA32
[i
].name_offset 
> maxSymbolOffset 
) { 
2726                         diag
.error("malformed import table, string overflow"); 
2729                     uint8_t libVal 
= importsA32
[i
].lib_ordinal
; 
2730                     if ( libVal 
> 0xF0 ) 
2731                         libOrdinal 
= (int8_t)libVal
; 
2733                         libOrdinal 
= libVal
; 
2734                     callback(libOrdinal
, symbolName
, importsA32
[i
].addend
, importsA32
[i
].weak_import
, stop
); 
2737             case DYLD_CHAINED_IMPORT_ADDEND64
: 
2738                 importsA64 
= (dyld_chained_import_addend64
*)((uint8_t*)header 
+ header
->imports_offset
); 
2739                 for (uint32_t i
=0; i 
< header
->imports_count 
&& !stop
; ++i
) { 
2740                     const char* symbolName 
= &symbolsPool
[importsA64
[i
].name_offset
]; 
2741                     if ( importsA64
[i
].name_offset 
> maxSymbolOffset 
) { 
2742                         diag
.error("malformed import table, string overflow"); 
2745                     uint16_t libVal 
= importsA64
[i
].lib_ordinal
; 
2746                     if ( libVal 
> 0xFFF0 ) 
2747                         libOrdinal 
= (int16_t)libVal
; 
2749                         libOrdinal 
= libVal
; 
2750                     callback(libOrdinal
, symbolName
, importsA64
[i
].addend
, importsA64
[i
].weak_import
, stop
); 
2754                 diag
.error("unknown imports format"); 
2760 uint32_t MachOAnalyzer::segmentCount() const 
2762     __block 
uint32_t count   
= 0; 
2763     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
2769 bool MachOAnalyzer::hasCodeSignature(uint32_t& fileOffset
, uint32_t& size
) const 
2775     forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { 
2776         if ( cmd
->cmd 
== LC_CODE_SIGNATURE 
) { 
2777             const linkedit_data_command
* sigCmd 
= (linkedit_data_command
*)cmd
; 
2778             fileOffset 
= sigCmd
->dataoff
; 
2779             size       
= sigCmd
->datasize
; 
2783     diag
.assertNoError();   // any malformations in the file should have been caught by earlier validate() call 
2785     // early exist if no LC_CODE_SIGNATURE 
2786     if ( fileOffset 
== 0 ) 
2789     // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools 
2790     if ( (this->cputype 
== CPU_TYPE_X86_64
) || (this->cputype 
== CPU_TYPE_I386
) ) { 
2791         __block 
bool foundPlatform 
= false; 
2792         __block 
bool badSignature  
= false; 
2793         forEachSupportedPlatform(^(Platform platform
, uint32_t minOS
, uint32_t sdk
) { 
2794             foundPlatform 
= true; 
2795             if ( (platform 
== Platform::macOS
) && (sdk 
< 0x000A0900) ) 
2796                 badSignature 
= true; 
2798         return foundPlatform 
&& !badSignature
; 
2804 bool MachOAnalyzer::hasProgramVars(Diagnostics
& diag
, uint32_t& progVarsOffset
) const 
2806     if ( this->filetype 
!= MH_EXECUTE 
) 
2809     // macOS 10.8+              program uses LC_MAIN and ProgramVars are in libdyld.dylib 
2810     // macOS 10.6 -> 10.7       ProgramVars are in __program_vars section in main executable 
2811     // macOS 10.5               ProgramVars are in __dyld section in main executable and 7 pointers in size 
2812     // macOS 10.4 and earlier   ProgramVars need to be looked up by name in nlist of main executable 
2816     if ( getEntry(offset
, usesCRT
) && usesCRT 
) { 
2817         // is pre-10.8 program 
2818         uint64_t sectionSize
; 
2819         if ( const void* progVarsSection 
= findSectionContent("__DATA", "__program_vars", sectionSize
) ) { 
2820             progVarsOffset 
= (uint32_t)((uint8_t*)progVarsSection 
- (uint8_t*)this); 
2823         else if ( const void* dyldSection 
= findSectionContent("__DATA", "__dyld", sectionSize
) ) { 
2824             if ( sectionSize 
>= 7*pointerSize() ) { 
2825                 progVarsOffset 
= (uint32_t)((uint8_t*)dyldSection 
- (uint8_t*)this) + 2*pointerSize(); 
2829         diag
.error("pre-macOS 10.5 binaries not supported"); 
2835 // Convert from a (possibly) live pointer to a vmAddr 
2836 uint64_t MachOAnalyzer::VMAddrConverter::convertToVMAddr(uint64_t value
) const { 
2837     if ( contentRebased 
) { 
2840         // The value may have been signed.  Strip the signature if that is the case 
2841 #if __has_feature(ptrauth_calls) 
2842         value 
= (uint64_t)__builtin_ptrauth_strip((void*)value
, ptrauth_key_asia
); 
2847     if ( chainedPointerFormat 
!= 0 ) { 
2848         auto* chainedValue 
= (MachOAnalyzer::ChainedFixupPointerOnDisk
*)&value
; 
2849         uint64_t targetRuntimeOffset
; 
2850         if ( chainedValue
->isRebase(chainedPointerFormat
, preferredLoadAddress
, targetRuntimeOffset
) ) { 
2851             value 
= preferredLoadAddress 
+ targetRuntimeOffset
; 
2856 #if !(BUILDING_LIBDYLD || BUILDING_DYLD) 
2857     typedef MachOAnalyzer::VMAddrConverter VMAddrConverter
; 
2858     if ( sharedCacheChainedPointerFormat 
!= VMAddrConverter::SharedCacheFormat::none 
) { 
2859         switch ( sharedCacheChainedPointerFormat 
) { 
2860             case VMAddrConverter::SharedCacheFormat::none
: 
2862             case VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi
: { 
2863                 const uint64_t   deltaMask    
= 0x00FFFF0000000000; 
2864                 const uint64_t   valueMask    
= ~deltaMask
; 
2865                 const uint64_t   valueAdd     
= preferredLoadAddress
; 
2866                 value 
= (value 
& valueMask
); 
2872             case VMAddrConverter::SharedCacheFormat::v3
: { 
2873                 // Just use the chained pointer format for arm64e 
2874                 auto* chainedValue 
= (MachOAnalyzer::ChainedFixupPointerOnDisk
*)&value
; 
2875                 uint64_t targetRuntimeOffset
; 
2876                 if ( chainedValue
->isRebase(DYLD_CHAINED_PTR_ARM64E
, preferredLoadAddress
, 
2877                                             targetRuntimeOffset
) ) { 
2878                     value 
= preferredLoadAddress 
+ targetRuntimeOffset
; 
2890 MachOAnalyzer::VMAddrConverter 
MachOAnalyzer::makeVMAddrConverter(bool contentRebased
) const { 
2891     MachOAnalyzer::VMAddrConverter vmAddrConverter
; 
2892     vmAddrConverter
.preferredLoadAddress   
= preferredLoadAddress(); 
2893     vmAddrConverter
.slide                  
= getSlide(); 
2894     vmAddrConverter
.chainedPointerFormat   
= hasChainedFixups() ? chainedPointerFormat() : 0; 
2895     vmAddrConverter
.contentRebased         
= contentRebased
; 
2896     return vmAddrConverter
; 
2899 bool MachOAnalyzer::hasInitializer(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, const void* dyldCache
) const 
2901     __block 
bool result 
= false; 
2902     forEachInitializer(diag
, vmAddrConverter
, ^(uint32_t offset
) { 
2908 void MachOAnalyzer::forEachInitializerPointerSection(Diagnostics
& diag
, void (^callback
)(uint32_t sectionOffset
, uint32_t sectionSize
, const uint8_t* content
, bool& stop
)) const 
2910     const unsigned ptrSize     
= pointerSize(); 
2911     const uint64_t baseAddress 
= preferredLoadAddress(); 
2912     const uint64_t slide       
= (uint64_t)this - baseAddress
; 
2913     forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& sectStop
) { 
2914         if ( (info
.sectFlags 
& SECTION_TYPE
) == S_MOD_INIT_FUNC_POINTERS 
) { 
2915             if ( (info
.sectSize 
% ptrSize
) != 0 ) { 
2916                 diag
.error("initializer section %s/%s has bad size", info
.segInfo
.segName
, info
.sectName
); 
2920             if ( malformedSectionRange 
) { 
2921                 diag
.error("initializer section %s/%s extends beyond its segment", info
.segInfo
.segName
, info
.sectName
); 
2925             const uint8_t* content 
= (uint8_t*)(info
.sectAddr 
+ slide
); 
2926             if ( ((long)content 
% ptrSize
) != 0 ) { 
2927                 diag
.error("initializer section %s/%s is not pointer aligned", info
.segInfo
.segName
, info
.sectName
); 
2931             callback((uint32_t)(info
.sectAddr 
- baseAddress
), (uint32_t)info
.sectSize
, content
, sectStop
); 
2936 struct VIS_HIDDEN SegmentRanges
 
2938     struct SegmentRange 
{ 
2939         uint64_t vmAddrStart
; 
2944     bool contains(uint64_t vmAddr
) const { 
2945         for (const SegmentRange
& range 
: segments
) { 
2946             if ( (range
.vmAddrStart 
<= vmAddr
) && (vmAddr 
< range
.vmAddrEnd
) ) 
2953     SegmentRange localAlloc
[1]; 
2956     dyld3::OverflowSafeArray
<SegmentRange
> segments 
{ localAlloc
, sizeof(localAlloc
) / sizeof(localAlloc
[0]) }; 
2959 void MachOAnalyzer::forEachInitializer(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, void (^callback
)(uint32_t offset
), const void* dyldCache
) const 
2961     __block SegmentRanges executableSegments
; 
2962     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
2963         if ( (info
.protections 
& VM_PROT_EXECUTE
) != 0 ) { 
2964             executableSegments
.segments
.push_back({ info
.vmAddr
, info
.vmAddr 
+ info
.vmSize
, (uint32_t)info
.fileSize 
}); 
2968     if (executableSegments
.segments
.empty()) { 
2969         diag
.error("no exeutable segments"); 
2973     uint64_t loadAddress 
= preferredLoadAddress(); 
2974     intptr_t slide 
= getSlide(); 
2976     // if dylib linked with -init linker option, that initializer is first 
2977     forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { 
2978         if ( cmd
->cmd 
== LC_ROUTINES 
) { 
2979             const routines_command
* routines 
= (routines_command
*)cmd
; 
2980             uint64_t dashInit 
= routines
->init_address
; 
2981             if ( executableSegments
.contains(dashInit
) ) 
2982                 callback((uint32_t)(dashInit 
- loadAddress
)); 
2984                 diag
.error("-init does not point within __TEXT segment"); 
2986         else if ( cmd
->cmd 
== LC_ROUTINES_64 
) { 
2987             const routines_command_64
* routines 
= (routines_command_64
*)cmd
; 
2988             uint64_t dashInit 
= routines
->init_address
; 
2989             if ( executableSegments
.contains(dashInit
) ) 
2990                 callback((uint32_t)(dashInit 
- loadAddress
)); 
2992                 diag
.error("-init does not point within __TEXT segment"); 
2996     // next any function pointers in mod-init section 
2997     const unsigned ptrSize          
= pointerSize(); 
2998     forEachInitializerPointerSection(diag
, ^(uint32_t sectionOffset
, uint32_t sectionSize
, const uint8_t* content
, bool& stop
) { 
2999         if ( ptrSize 
== 8 ) { 
3000             const uint64_t* initsStart 
= (uint64_t*)content
; 
3001             const uint64_t* initsEnd   
= (uint64_t*)((uint8_t*)content 
+ sectionSize
); 
3002             for (const uint64_t* p
=initsStart
; p 
< initsEnd
; ++p
) { 
3003                 uint64_t anInit 
= vmAddrConverter
.convertToVMAddr(*p
); 
3004                 if ( !executableSegments
.contains(anInit
) ) { 
3005                      diag
.error("initializer 0x%0llX does not point within executable segment", anInit
); 
3009                 callback((uint32_t)(anInit 
- loadAddress
)); 
3013             const uint32_t* initsStart 
= (uint32_t*)content
; 
3014             const uint32_t* initsEnd   
= (uint32_t*)((uint8_t*)content 
+ sectionSize
); 
3015             for (const uint32_t* p
=initsStart
; p 
< initsEnd
; ++p
) { 
3016                 uint32_t anInit 
= (uint32_t)vmAddrConverter
.convertToVMAddr(*p
); 
3017                 if ( !executableSegments
.contains(anInit
) ) { 
3018                      diag
.error("initializer 0x%0X does not point within executable segment", anInit
); 
3022                 callback(anInit 
- (uint32_t)loadAddress
); 
3027     forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) { 
3028         if ( (info
.sectFlags 
& SECTION_TYPE
) != S_INIT_FUNC_OFFSETS 
) 
3030         const uint8_t* content 
= (uint8_t*)(info
.sectAddr 
+ slide
); 
3031         if ( info
.segInfo
.writable() ) { 
3032             diag
.error("initializer offsets section %s/%s must be in read-only segment", info
.segInfo
.segName
, info
.sectName
); 
3036         if ( (info
.sectSize 
% 4) != 0 ) { 
3037             diag
.error("initializer offsets section %s/%s has bad size", info
.segInfo
.segName
, info
.sectName
); 
3041         if ( malformedSectionRange 
) { 
3042             diag
.error("initializer offsets section %s/%s extends beyond the end of the segment", info
.segInfo
.segName
, info
.sectName
); 
3046         if ( (info
.sectAddr 
% 4) != 0 ) { 
3047             diag
.error("initializer offsets section %s/%s is not 4-byte aligned", info
.segInfo
.segName
, info
.sectName
); 
3051         const uint32_t* initsStart 
= (uint32_t*)content
; 
3052         const uint32_t* initsEnd   
= (uint32_t*)((uint8_t*)content 
+ info
.sectSize
); 
3053         for (const uint32_t* p
=initsStart
; p 
< initsEnd
; ++p
) { 
3054             uint32_t anInitOffset 
= *p
; 
3055             if ( !executableSegments
.contains(loadAddress 
+ anInitOffset
) ) { 
3056                  diag
.error("initializer 0x%08X does not an offset to an executable segment", anInitOffset
); 
3060             callback(anInitOffset
); 
3065 bool MachOAnalyzer::hasTerminators(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
) const 
3067     __block 
bool result 
= false; 
3068     forEachTerminator(diag
, vmAddrConverter
, ^(uint32_t offset
) { 
3074 void MachOAnalyzer::forEachTerminator(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, void (^callback
)(uint32_t offset
)) const 
3076     __block SegmentRanges executableSegments
; 
3077     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
3078         if ( (info
.protections 
& VM_PROT_EXECUTE
) != 0 ) { 
3079             executableSegments
.segments
.push_back({ info
.vmAddr
, info
.vmAddr 
+ info
.vmSize
, (uint32_t)info
.fileSize 
}); 
3083     if (executableSegments
.segments
.empty()) { 
3084         diag
.error("no exeutable segments"); 
3088     uint64_t loadAddress 
= preferredLoadAddress(); 
3089     intptr_t slide 
= getSlide(); 
3091     // next any function pointers in mod-term section 
3092     const unsigned ptrSize          
= pointerSize(); 
3093     forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) { 
3094         if ( (info
.sectFlags 
& SECTION_TYPE
) == S_MOD_TERM_FUNC_POINTERS 
) { 
3095             const uint8_t* content
; 
3096             content 
= (uint8_t*)(info
.sectAddr 
+ slide
); 
3097             if ( (info
.sectSize 
% ptrSize
) != 0 ) { 
3098                 diag
.error("terminator section %s/%s has bad size", info
.segInfo
.segName
, info
.sectName
); 
3102             if ( malformedSectionRange 
) { 
3103                 diag
.error("terminator section %s/%s extends beyond its segment", info
.segInfo
.segName
, info
.sectName
); 
3107             if ( ((long)content 
% ptrSize
) != 0 ) { 
3108                 diag
.error("terminator section %s/%s is not pointer aligned", info
.segInfo
.segName
, info
.sectName
); 
3112             if ( ptrSize 
== 8 ) { 
3113                 const uint64_t* initsStart 
= (uint64_t*)content
; 
3114                 const uint64_t* initsEnd   
= (uint64_t*)((uint8_t*)content 
+ info
.sectSize
); 
3115                 for (const uint64_t* p
=initsStart
; p 
< initsEnd
; ++p
) { 
3116                     uint64_t anInit 
= vmAddrConverter
.convertToVMAddr(*p
); 
3117                     if ( !executableSegments
.contains(anInit
) ) { 
3118                          diag
.error("terminator 0x%0llX does not point within executable segment", anInit
); 
3122                     callback((uint32_t)(anInit 
- loadAddress
)); 
3126                 const uint32_t* initsStart 
= (uint32_t*)content
; 
3127                 const uint32_t* initsEnd   
= (uint32_t*)((uint8_t*)content 
+ info
.sectSize
); 
3128                 for (const uint32_t* p
=initsStart
; p 
< initsEnd
; ++p
) { 
3129                     uint32_t anInit 
= (uint32_t)vmAddrConverter
.convertToVMAddr(*p
); 
3130                     if ( !executableSegments
.contains(anInit
) ) { 
3131                          diag
.error("terminator 0x%0X does not point within executable segment", anInit
); 
3135                     callback(anInit 
- (uint32_t)loadAddress
); 
3144 void MachOAnalyzer::forEachRPath(void (^callback
)(const char* rPath
, bool& stop
)) const 
3147     forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { 
3148          if ( cmd
->cmd 
== LC_RPATH 
) { 
3149             const char* rpath 
= (char*)cmd 
+ ((struct rpath_command
*)cmd
)->path
.offset
; 
3150             callback(rpath
, stop
); 
3153     diag
.assertNoError();   // any malformations in the file should have been caught by earlier validate() call 
3157 bool MachOAnalyzer::hasObjC() const 
3159     __block 
bool result 
= false; 
3160     forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) { 
3161         if ( (strcmp(info
.sectName
, "__objc_imageinfo") == 0) && (strncmp(info
.segInfo
.segName
, "__DATA", 6) == 0) ) { 
3165         if ( (this->cputype 
== CPU_TYPE_I386
) && (strcmp(info
.sectName
, "__image_info") == 0) && (strcmp(info
.segInfo
.segName
, "__OBJC") == 0) ) { 
3173 bool MachOAnalyzer::usesObjCGarbageCollection() const 
3175     __block 
bool result 
= false; 
3176     forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) { 
3177         if ( (strcmp(info
.sectName
, "__objc_imageinfo") == 0) && (strncmp(info
.segInfo
.segName
, "__DATA", 6) == 0) ) { 
3178             const uint64_t  slide 
= (uint64_t)this - preferredLoadAddress(); 
3179             const uint32_t* flags 
= (uint32_t*)(info
.sectAddr 
+ slide
); 
3189 bool MachOAnalyzer::hasPlusLoadMethod(Diagnostics
& diag
) const 
3191     __block 
bool result 
= false; 
3192     if ( (this->cputype 
== CPU_TYPE_I386
) && this->builtForPlatform(Platform::macOS
) ) { 
3193         // old objc runtime has no special section for +load methods, scan for string 
3194         int64_t slide 
= getSlide(); 
3195         forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) { 
3196             if ( ( (info
.sectFlags 
& SECTION_TYPE
) == S_CSTRING_LITERALS 
) ) { 
3197                 if ( malformedSectionRange 
) { 
3198                     diag
.error("cstring section %s/%s extends beyond the end of the segment", info
.segInfo
.segName
, info
.sectName
); 
3202                 const uint8_t* content 
= (uint8_t*)(info
.sectAddr 
+ slide
); 
3203                 const char* s   
= (char*)content
; 
3204                 const char* end 
= s 
+ info
.sectSize
; 
3206                     if ( strcmp(s
, "load") == 0 ) { 
3219         // in new objc runtime compiler puts classes/categories with +load method in specical section 
3220         forEachSection(^(const SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) { 
3221             if ( strncmp(info
.segInfo
.segName
, "__DATA", 6) != 0 ) 
3223             if ( (strcmp(info
.sectName
, "__objc_nlclslist") == 0) || (strcmp(info
.sectName
, "__objc_nlcatlist") == 0)) { 
3232 bool MachOAnalyzer::isSwiftLibrary() const 
3234     struct objc_image_info 
{ 
3239     int64_t slide 
= getSlide(); 
3240     __block 
bool result 
= false; 
3241     forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
3242         if ( (strncmp(sectInfo
.sectName
, "__objc_imageinfo", 16) == 0) && (strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) == 0) ) { 
3243             objc_image_info
* info 
=  (objc_image_info
*)((uint8_t*)sectInfo
.sectAddr 
+ slide
); 
3244             uint32_t swiftVersion 
= ((info
->flags 
>> 8) & 0xFF); 
3253 const void* MachOAnalyzer::getRebaseOpcodes(uint32_t& size
) const 
3256     LinkEditInfo leInfo
; 
3257     getLinkEditPointers(diag
, leInfo
); 
3258     if ( diag
.hasError() || (leInfo
.dyldInfo 
== nullptr) ) 
3261     size 
= leInfo
.dyldInfo
->rebase_size
; 
3262     return getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->rebase_off
); 
3265 const void* MachOAnalyzer::getBindOpcodes(uint32_t& size
) const 
3268     LinkEditInfo leInfo
; 
3269     getLinkEditPointers(diag
, leInfo
); 
3270     if ( diag
.hasError() || (leInfo
.dyldInfo 
== nullptr) ) 
3273     size 
= leInfo
.dyldInfo
->bind_size
; 
3274     return getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->bind_off
); 
3277 const void* MachOAnalyzer::getLazyBindOpcodes(uint32_t& size
) const 
3280     LinkEditInfo leInfo
; 
3281     getLinkEditPointers(diag
, leInfo
); 
3282     if ( diag
.hasError() || (leInfo
.dyldInfo 
== nullptr) ) 
3285     size 
= leInfo
.dyldInfo
->lazy_bind_size
; 
3286     return getLinkEditContent(leInfo
.layout
, leInfo
.dyldInfo
->lazy_bind_off
); 
3289 const void* MachOAnalyzer::getSplitSeg(uint32_t& size
) const 
3292     LinkEditInfo leInfo
; 
3293     getLinkEditPointers(diag
, leInfo
); 
3294     if ( diag
.hasError() || (leInfo
.splitSegInfo 
== nullptr) ) 
3297     size 
= leInfo
.splitSegInfo
->datasize
; 
3298     return getLinkEditContent(leInfo
.layout
, leInfo
.splitSegInfo
->dataoff
); 
3301 bool MachOAnalyzer::hasSplitSeg() const { 
3302     uint32_t splitSegSize 
= 0; 
3303     const void* splitSegStart 
= getSplitSeg(splitSegSize
); 
3304     return splitSegStart 
!= nullptr; 
3307 bool MachOAnalyzer::isSplitSegV1() const { 
3308     uint32_t splitSegSize 
= 0; 
3309     const void* splitSegStart 
= getSplitSeg(splitSegSize
); 
3313     return (*(const uint8_t*)splitSegStart
) != DYLD_CACHE_ADJ_V2_FORMAT
; 
3316 bool MachOAnalyzer::isSplitSegV2() const { 
3317     uint32_t splitSegSize 
= 0; 
3318     const void* splitSegStart 
= getSplitSeg(splitSegSize
); 
3322     return (*(const uint8_t*)splitSegStart
) == DYLD_CACHE_ADJ_V2_FORMAT
; 
3326 uint64_t MachOAnalyzer::segAndOffsetToRuntimeOffset(uint8_t targetSegIndex
, uint64_t targetSegOffset
) const 
3328     __block 
uint64_t textVmAddr 
= 0; 
3329     __block 
uint64_t result     
= 0; 
3330     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
3331         if ( strcmp(info
.segName
, "__TEXT") == 0 ) 
3332             textVmAddr 
= info
.vmAddr
; 
3333         if ( info
.segIndex 
== targetSegIndex 
) { 
3334             result 
= (info
.vmAddr 
- textVmAddr
) + targetSegOffset
; 
3340 bool MachOAnalyzer::hasLazyPointers(uint32_t& runtimeOffset
, uint32_t& size
) const 
3343     forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) { 
3344         if ( (info
.sectFlags 
& SECTION_TYPE
) == S_LAZY_SYMBOL_POINTERS 
) { 
3345             runtimeOffset 
= (uint32_t)(info
.sectAddr 
- preferredLoadAddress()); 
3346             size          
= (uint32_t)info
.sectSize
; 
3353 uint64_t MachOAnalyzer::preferredLoadAddress() const 
3355     __block 
uint64_t textVmAddr 
= 0; 
3356     forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
3357         if ( strcmp(info
.segName
, "__TEXT") == 0 ) { 
3358             textVmAddr 
= info
.vmAddr
; 
3366 bool MachOAnalyzer::getEntry(uint64_t& offset
, bool& usesCRT
) const 
3370     forEachLoadCommand(diag
, ^(const load_command
* cmd
, bool& stop
) { 
3371         if ( cmd
->cmd 
== LC_MAIN 
) { 
3372             entry_point_command
* mainCmd 
= (entry_point_command
*)cmd
; 
3374             offset 
= mainCmd
->entryoff
; 
3377         else if ( cmd
->cmd 
== LC_UNIXTHREAD 
) { 
3380             uint64_t startAddress 
= entryAddrFromThreadCmd((thread_command
*)cmd
); 
3381             offset 
= startAddress 
- preferredLoadAddress(); 
3384     return (offset 
!= 0); 
3388 void MachOAnalyzer::forEachInterposingSection(Diagnostics
& diag
, void (^handler
)(uint64_t vmOffset
, uint64_t vmSize
, bool& stop
)) const 
3390     const unsigned ptrSize   
= pointerSize(); 
3391     const unsigned entrySize 
= 2 * ptrSize
; 
3392     forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) { 
3393         if ( ((info
.sectFlags 
& SECTION_TYPE
) == S_INTERPOSING
) || ((strcmp(info
.sectName
, "__interpose") == 0) && (strcmp(info
.segInfo
.segName
, "__DATA") == 0)) ) { 
3394             if ( info
.sectSize 
% entrySize 
!= 0 ) { 
3395                 diag
.error("interposing section %s/%s has bad size", info
.segInfo
.segName
, info
.sectName
); 
3399             if ( malformedSectionRange 
) { 
3400                 diag
.error("interposing section %s/%s extends beyond the end of the segment", info
.segInfo
.segName
, info
.sectName
); 
3404             if ( (info
.sectAddr 
% ptrSize
) != 0 ) { 
3405                 diag
.error("interposing section %s/%s is not pointer aligned", info
.segInfo
.segName
, info
.sectName
); 
3409             handler(info
.sectAddr 
- preferredLoadAddress(), info
.sectSize
, stop
); 
3414 void MachOAnalyzer::forEachDOFSection(Diagnostics
& diag
, void (^callback
)(uint32_t offset
)) const 
3416     forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) { 
3417         if ( ( (info
.sectFlags 
& SECTION_TYPE
) == S_DTRACE_DOF 
) && !malformedSectionRange 
) { 
3418             callback((uint32_t)(info
.sectAddr 
- info
.segInfo
.vmAddr
)); 
3423 void MachOAnalyzer::forEachCDHash(void (^handler
)(const uint8_t cdHash
[20])) const 
3426     LinkEditInfo leInfo
; 
3427     getLinkEditPointers(diag
, leInfo
); 
3428     if ( diag
.hasError() || (leInfo
.codeSig 
== nullptr) ) 
3431     forEachCDHashOfCodeSignature(getLinkEditContent(leInfo
.layout
, leInfo
.codeSig
->dataoff
), 
3432                                  leInfo
.codeSig
->datasize
, 
3436 bool MachOAnalyzer::isRestricted() const 
3438     __block 
bool result 
= false; 
3439     forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool &stop
) { 
3440         if ( (strcmp(info
.segInfo
.segName
, "__RESTRICT") == 0) && (strcmp(info
.sectName
, "__restrict") == 0) ) { 
3448 bool MachOAnalyzer::usesLibraryValidation() const 
3451     LinkEditInfo leInfo
; 
3452     getLinkEditPointers(diag
, leInfo
); 
3453     if ( diag
.hasError() || (leInfo
.codeSig 
== nullptr) ) 
3456     // check for CS_REQUIRE_LV in CS_CodeDirectory.flags 
3457     __block 
bool requiresLV 
= false; 
3458     forEachCodeDirectoryBlob(getLinkEditContent(leInfo
.layout
, leInfo
.codeSig
->dataoff
), 
3459                              leInfo
.codeSig
->datasize
, 
3460                              ^(const void *cdBuffer
) { 
3461          const CS_CodeDirectory
* cd 
= (const CS_CodeDirectory
*)cdBuffer
; 
3462          requiresLV 
|= (htonl(cd
->flags
) & CS_REQUIRE_LV
); 
3468 bool MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path
, void (^failureReason
)(const char*)) const 
3470     if (!MachOFile::canHavePrecomputedDlopenClosure(path
, failureReason
)) 
3473     // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same 
3474     // at runtime as when the shared cache processed it.  We must have a code signature to record this information 
3475     uint32_t codeSigFileOffset
; 
3476     uint32_t codeSigSize
; 
3477     if ( !hasCodeSignature(codeSigFileOffset
, codeSigSize
) ) { 
3478         failureReason("no code signature"); 
3482     __block 
bool retval 
= true; 
3484     // images that use dynamic_lookup, bundle_loader, or have weak-defs cannot have dlopen closure pre-computed 
3486     auto checkBind 
= ^(int libOrdinal
, bool& stop
) { 
3487         switch (libOrdinal
) { 
3488             case BIND_SPECIAL_DYLIB_WEAK_LOOKUP
: 
3489                 failureReason("has weak externals"); 
3493             case BIND_SPECIAL_DYLIB_FLAT_LOOKUP
: 
3494                 failureReason("has dynamic_lookup binds"); 
3498             case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
: 
3499                 failureReason("has reference to main executable (bundle loader)"); 
3506     if (hasChainedFixups()) { 
3507         forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char *symbolName
, uint64_t addend
, bool weakImport
, bool &stop
) { 
3508             checkBind(libOrdinal
, stop
); 
3511         forEachBind(diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) { 
3512             checkBind(libOrdinal
, stop
); 
3514         ^(const char* symbolName
) { 
3522 bool MachOAnalyzer::hasUnalignedPointerFixups() const 
3524     // only look at 64-bit architectures 
3525     if ( pointerSize() == 4 ) 
3528     __block Diagnostics diag
; 
3529     __block 
bool result 
= false; 
3530     if ( hasChainedFixups() ) { 
3531         withChainStarts(diag
, chainStartsOffset(), ^(const dyld_chained_starts_in_image
* startsInfo
) { 
3532             forEachFixupInAllChains(diag
, startsInfo
, false, ^(MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& fixupsStop
) { 
3533                 if ( ((long)(fixupLoc
) & 7) != 0 ) { 
3541         forEachBind(diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) { 
3542             if ( (runtimeOffset 
& 7) != 0 ) { 
3547         ^(const char* symbolName
) { 
3549         forEachRebase(diag
, true, ^(uint64_t runtimeOffset
, bool& stop
) { 
3550             if ( (runtimeOffset 
& 7) != 0 ) { 
3560 void MachOAnalyzer::recurseTrie(Diagnostics
& diag
, const uint8_t* const start
, const uint8_t* p
, const uint8_t* const end
, 
3561                                 OverflowSafeArray
<char>& cummulativeString
, int curStrOffset
, bool& stop
, ExportsCallback callback
) const 
3564         diag
.error("malformed trie, node past end"); 
3567     const uint64_t terminalSize 
= read_uleb128(diag
, p
, end
); 
3568     const uint8_t* children 
= p 
+ terminalSize
; 
3569     if ( terminalSize 
!= 0 ) { 
3570         uint64_t    imageOffset 
= 0; 
3571         uint64_t    flags       
= read_uleb128(diag
, p
, end
); 
3573         const char* importName  
= nullptr; 
3574         if ( flags 
& EXPORT_SYMBOL_FLAGS_REEXPORT 
) { 
3575             other 
= read_uleb128(diag
, p
, end
); // dylib ordinal 
3576             importName 
= (char*)p
; 
3579             imageOffset 
= read_uleb128(diag
, p
, end
); 
3580             if ( flags 
& EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 
) 
3581                 other 
= read_uleb128(diag
, p
, end
); 
3585         if ( diag
.hasError() ) 
3587         callback(cummulativeString
.begin(), imageOffset
, flags
, other
, importName
, stop
); 
3591     if ( children 
> end 
) { 
3592         diag
.error("malformed trie, terminalSize extends beyond trie data"); 
3595     const uint8_t childrenCount 
= *children
++; 
3596     const uint8_t* s 
= children
; 
3597     for (uint8_t i
=0; i 
< childrenCount
; ++i
) { 
3599         while (*s 
!= '\0') { 
3600             cummulativeString
.resize(curStrOffset
+edgeStrLen 
+ 1); 
3601             cummulativeString
[curStrOffset
+edgeStrLen
] = *s
++; 
3604                 diag
.error("malformed trie node, child node extends past end of trie\n"); 
3608         cummulativeString
.resize(curStrOffset
+edgeStrLen 
+ 1); 
3609         cummulativeString
[curStrOffset
+edgeStrLen
] = *s
++; 
3610         uint64_t childNodeOffset 
= read_uleb128(diag
, s
, end
); 
3611         if (childNodeOffset 
== 0) { 
3612             diag
.error("malformed trie, childNodeOffset==0"); 
3615         recurseTrie(diag
, start
, start
+childNodeOffset
, end
, cummulativeString
, curStrOffset
+edgeStrLen
, stop
, callback
); 
3616         if ( diag
.hasError() || stop 
) 
3621 void MachOAnalyzer::forEachExportedSymbol(Diagnostics
& diag
, ExportsCallback callback
) const 
3623     LinkEditInfo leInfo
; 
3624     getLinkEditPointers(diag
, leInfo
); 
3625     if ( diag
.hasError() ) 
3628     if ( const uint8_t* trieStart 
= getExportsTrie(leInfo
, trieSize
) ) { 
3629         const uint8_t* trieEnd   
= trieStart 
+ trieSize
; 
3630         // We still emit empty export trie load commands just as a placeholder to show we have 
3631         // no exports.  In that case, don't start recursing as we'll immediately think we ran 
3632         // of the end of the buffer 
3633         if ( trieSize 
== 0 ) 
3636         STACK_ALLOC_OVERFLOW_SAFE_ARRAY(char, cummulativeString
, 4096); 
3637         recurseTrie(diag
, trieStart
, trieStart
, trieEnd
, cummulativeString
, 0, stop
, callback
); 
3641 bool MachOAnalyzer::markNeverUnload(Diagnostics 
&diag
) const { 
3642     bool neverUnload 
= false; 
3644     if ( hasThreadLocalVariables() ) { 
3646     } else if ( hasObjC() && isDylib() ) { 
3649         // record if image has DOF sections 
3650         __block 
bool hasDOFs 
= false; 
3651         forEachDOFSection(diag
, ^(uint32_t offset
) { 
3661 bool MachOAnalyzer::canBePlacedInDyldCache(const char* path
, void (^failureReason
)(const char*)) const 
3663     if (!MachOFile::canBePlacedInDyldCache(path
, failureReason
)) 
3666     // arm64e requires split seg v2 as the split seg code can't handle chained fixups for split seg v1 
3667     if ( isArch("arm64e") ) { 
3668         uint32_t splitSegSize 
= 0; 
3669         const uint8_t* infoStart 
= (const uint8_t*)getSplitSeg(splitSegSize
); 
3670         if ( *infoStart 
!= DYLD_CACHE_ADJ_V2_FORMAT 
) { 
3671             failureReason("chained fixups requires split seg v2"); 
3676     // <rdar://problem/57769033> dyld_cache_patchable_location only supports addend in range 0..31 
3677     const bool is64bit 
= is64(); 
3678     __block Diagnostics diag
; 
3679     __block 
bool addendTooLarge 
= false; 
3680     if ( this->hasChainedFixups() ) { 
3681         // with chained fixups, addends can be in the import table or embedded in a bind pointer 
3682         forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) { 
3684                 addend 
&= 0x00FFFFFFFFFFFFFF; // ignore TBI 
3685             if ( addend 
> 31 ) { 
3686                 addendTooLarge 
= true; 
3690         // check each pointer for embedded addend 
3691         withChainStarts(diag
, 0, ^(const dyld_chained_starts_in_image
* starts
) { 
3692             forEachFixupInAllChains(diag
, starts
, false, ^(ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& stop
) { 
3693                 switch (segInfo
->pointer_format
) { 
3694                     case DYLD_CHAINED_PTR_ARM64E
: 
3695                     case DYLD_CHAINED_PTR_ARM64E_USERLAND
: 
3696                     case DYLD_CHAINED_PTR_ARM64E_USERLAND24
: 
3697                         if ( fixupLoc
->arm64e
.bind
.bind 
&& !fixupLoc
->arm64e
.authBind
.auth 
) { 
3698                             if ( fixupLoc
->arm64e
.bind
.addend 
> 31 ) { 
3699                                 addendTooLarge 
= true; 
3704                     case DYLD_CHAINED_PTR_64
: 
3705                     case DYLD_CHAINED_PTR_64_OFFSET
: 
3706                         if ( fixupLoc
->generic64
.rebase
.bind 
) { 
3707                             if ( fixupLoc
->generic64
.bind
.addend 
> 31 ) { 
3708                                 addendTooLarge 
= true; 
3713                     case DYLD_CHAINED_PTR_32
: 
3714                         if ( fixupLoc
->generic32
.bind
.bind 
) { 
3715                             if ( fixupLoc
->generic32
.bind
.addend 
> 31 ) { 
3716                                 addendTooLarge 
= true; 
3726         // scan bind opcodes for large addend 
3727         forEachBind(diag
, ^(const char* opcodeName
, const LinkEditInfo
& leInfo
, const SegmentInfo
* segments
, bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
, 
3728                             uint32_t ptrSize
, uint8_t segmentIndex
, uint64_t segmentOffset
, uint8_t type
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) { 
3730                 addend 
&= 0x00FFFFFFFFFFFFFF; // ignore TBI 
3731             if ( addend 
> 31 ) { 
3732                 addendTooLarge 
= true; 
3736         ^(const char* symbolName
) { 
3739     if ( addendTooLarge 
) { 
3740         failureReason("bind addend too large"); 
3744     // evict swift dylibs with split seg v1 info 
3745     if ( this->isSwiftLibrary() && this->isSplitSegV1() ) 
3748     if ( hasChainedFixups() ) { 
3749         // Chained fixups assumes split seg v2.  This is true for now as chained fixups is arm64e only 
3750         return this->isSplitSegV2(); 
3753     if ( !(isArch("x86_64") || isArch("x86_64h")) ) 
3756     __block 
bool rebasesOk 
= true; 
3757     uint64_t startVMAddr 
= preferredLoadAddress(); 
3758     uint64_t endVMAddr 
= startVMAddr 
+ mappedSize(); 
3759     forEachRebase(diag
, false, ^(uint64_t runtimeOffset
, bool &stop
) { 
3760         // We allow TBI for x86_64 dylibs, but then require that the remainder of the offset 
3761         // is a 32-bit offset from the mach-header. 
3762         uint64_t value 
= *(uint64_t*)((uint8_t*)this + runtimeOffset
); 
3763         value 
&= 0x00FFFFFFFFFFFFFFULL
; 
3764         if ( (value 
< startVMAddr
) || (value 
>= endVMAddr
) ) { 
3765             failureReason("rebase value out of range of dylib"); 
3771         // Also error if the rebase location is anything other than 4/8 byte aligned 
3772         if ( (runtimeOffset 
& 0x3) != 0 ) { 
3773             failureReason("rebase value is not 4-byte aligned"); 
3782 #if BUILDING_APP_CACHE_UTIL 
3783 bool MachOAnalyzer::canBePlacedInKernelCollection(const char* path
, void (^failureReason
)(const char*)) const 
3785     if (!MachOFile::canBePlacedInKernelCollection(path
, failureReason
)) 
3788     // App caches reguire that everything be built with split seg v2 
3789     // This is because v1 can't move anything other than __TEXT and __DATA 
3790     // but kernels have __TEXT_EXEC and other segments 
3791     if ( isKextBundle() ) { 
3792         // x86_64 kext's might not have split seg 
3793         if ( !isArch("x86_64") && !isArch("x86_64h") ) { 
3794             if ( !isSplitSegV2() ) { 
3795                 failureReason("Missing split seg v2"); 
3799     } else if ( isStaticExecutable() ) { 
3800         // The kernel must always have split seg V2 
3801         if ( !isSplitSegV2() ) { 
3802             failureReason("Missing split seg v2"); 
3806         // The kernel should have __TEXT and __TEXT_EXEC 
3807         __block 
bool foundText 
= false; 
3808         __block 
bool foundTextExec 
= false; 
3809         __block 
bool foundHIB 
= false; 
3810         __block 
uint64_t hibernateVMAddr 
= 0; 
3811         __block 
uint64_t hibernateVMSize 
= 0; 
3812         forEachSegment(^(const SegmentInfo 
&segmentInfo
, bool &stop
) { 
3813             if ( strcmp(segmentInfo
.segName
, "__TEXT") == 0 ) { 
3816             if ( strcmp(segmentInfo
.segName
, "__TEXT_EXEC") == 0 ) { 
3817                 foundTextExec 
= true; 
3819             if ( strcmp(segmentInfo
.segName
, "__HIB") == 0 ) { 
3821                 hibernateVMAddr 
= segmentInfo
.vmAddr
; 
3822                 hibernateVMSize 
= segmentInfo
.vmSize
; 
3826             failureReason("Expected __TEXT segment"); 
3829         if ( foundTextExec 
&& foundHIB 
) { 
3830             failureReason("Expected __TEXT_EXEC or __HIB segment, but found both"); 
3833         if ( !foundTextExec 
&& !foundHIB 
) { 
3834             failureReason("Expected __TEXT_EXEC or __HIB segment, but found neither"); 
3838         // The hibernate segment should be mapped before the base address 
3840             uint64_t baseAddress 
= preferredLoadAddress(); 
3841             if ( greaterThanAddOrOverflow(hibernateVMAddr
, hibernateVMSize
, baseAddress
) ) { 
3842                 failureReason("__HIB segment should be mapped before base address"); 
3848     // Don't allow kext's to have load addresses 
3849     if ( isKextBundle() && (preferredLoadAddress() != 0) ) { 
3850         failureReason("Has load address"); 
3854     if (hasChainedFixups()) { 
3855         if ( usesClassicRelocationsInKernelCollection() ) { 
3856             failureReason("Cannot use fixup chains with binary expecting classic relocations"); 
3860         __block 
bool fixupsOk 
= true; 
3861         __block Diagnostics diag
; 
3862         withChainStarts(diag
, 0, ^(const dyld_chained_starts_in_image
* starts
) { 
3863             forEachFixupInAllChains(diag
, starts
, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, 
3864                                                            const dyld_chained_starts_in_segment
* segInfo
, bool& stop
) { 
3865                 // We only support inputs from a few pointer format types, so that we don't need to handle them all later 
3866                 switch (segInfo
->pointer_format
) { 
3867                     case DYLD_CHAINED_PTR_ARM64E
: 
3868                     case DYLD_CHAINED_PTR_64
: 
3869                     case DYLD_CHAINED_PTR_32
: 
3870                     case DYLD_CHAINED_PTR_32_CACHE
: 
3871                     case DYLD_CHAINED_PTR_32_FIRMWARE
: 
3872                         failureReason("unsupported chained fixups pointer format"); 
3876                     case DYLD_CHAINED_PTR_64_OFFSET
: 
3877                         // arm64 kernel and kexts use this format 
3879                     case DYLD_CHAINED_PTR_ARM64E_KERNEL
: 
3880                         // arm64e kexts use this format 
3882                     case DYLD_CHAINED_PTR_64_KERNEL_CACHE
: 
3883                     case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE
: 
3884                         failureReason("unsupported chained fixups pointer format"); 
3889                         failureReason("unknown chained fixups pointer format"); 
3895                 uint64_t vmOffset 
= (uint8_t*)fixupLoc 
- (uint8_t*)this; 
3896                 // Error if the fixup location is anything other than 4/8 byte aligned 
3897                 if ( (vmOffset 
& 0x3) != 0 ) { 
3898                     failureReason("fixup value is not 4-byte aligned"); 
3904                 // We also must only need 30-bits for the chain format of the resulting cache 
3905                 if ( vmOffset 
>= (1 << 30) ) { 
3906                     failureReason("fixup value does not fit in 30-bits"); 
3916         // x86_64 xnu will have unaligned text/data fixups and fixups inside __HIB __text. 
3917         // We allow these as xnu is emitted with classic relocations 
3918         bool canHaveUnalignedFixups 
= usesClassicRelocationsInKernelCollection(); 
3919         canHaveUnalignedFixups 
|= ( isArch("x86_64") || isArch("x86_64h") ); 
3920         __block 
bool rebasesOk 
= true; 
3922         forEachRebase(diag
, false, ^(uint64_t runtimeOffset
, bool &stop
) { 
3923             // Error if the rebase location is anything other than 4/8 byte aligned 
3924             if ( !canHaveUnalignedFixups 
&& ((runtimeOffset 
& 0x3) != 0) ) { 
3925                 failureReason("rebase value is not 4-byte aligned"); 
3931 #if BUILDING_APP_CACHE_UTIL 
3932             // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be 
3933             // negative.  Adjust the fixups so that we don't think they are out of 
3934             // range of the number of bits we have 
3935             if ( isStaticExecutable() ) { 
3936                 __block 
uint64_t baseAddress 
= ~0ULL; 
3937                 forEachSegment(^(const SegmentInfo
& info
, bool& stop
) { 
3938                     baseAddress 
= std::min(baseAddress
, info
.vmAddr
); 
3940                 uint64_t textSegVMAddr 
= preferredLoadAddress(); 
3941                 runtimeOffset 
= (textSegVMAddr 
+ runtimeOffset
) - baseAddress
; 
3945             // We also must only need 30-bits for the chain format of the resulting cache 
3946             if ( runtimeOffset 
>= (1 << 30) ) { 
3947                 failureReason("rebase value does not fit in 30-bits"); 
3956         __block 
bool bindsOk 
= true; 
3957         forEachBind(diag
, ^(uint64_t runtimeOffset
, int libOrdinal
, uint8_t type
, const char *symbolName
, 
3958                             bool weakImport
, bool lazyBind
, uint64_t addend
, bool &stop
) { 
3960             // Don't validate branch fixups as we'll turn then in to direct jumps instead 
3961             if ( type 
== BIND_TYPE_TEXT_PCREL32 
) 
3964             // Error if the bind location is anything other than 4/8 byte aligned 
3965             if ( !canHaveUnalignedFixups 
&& ((runtimeOffset 
& 0x3) != 0) ) { 
3966                 failureReason("bind value is not 4-byte aligned"); 
3972             // We also must only need 30-bits for the chain format of the resulting cache 
3973             if ( runtimeOffset 
>= (1 << 30) ) { 
3974                 failureReason("bind value does not fit in 30-bits"); 
3979         }, ^(const char *symbolName
) { 
3990 bool MachOAnalyzer::usesClassicRelocationsInKernelCollection() const { 
3991     // The xnu x86_64 static executable needs to do the i386->x86_64 transition 
3992     // so will be emitted with classic relocations 
3993     if ( isArch("x86_64") || isArch("x86_64h") ) { 
3994         return isStaticExecutable() || isFileSet(); 
3999 uint64_t MachOAnalyzer::chainStartsOffset() const 
4001     const dyld_chained_fixups_header
* header 
= chainedFixupsHeader(); 
4002     // old arm64e binary has no dyld_chained_fixups_header 
4003     if ( header 
== nullptr ) 
4005     return header
->starts_offset 
+ ((uint8_t*)header 
- (uint8_t*)this); 
4008 const dyld_chained_fixups_header
* MachOAnalyzer::chainedFixupsHeader() const 
4011     LinkEditInfo leInfo
; 
4012     getLinkEditPointers(diag
, leInfo
); 
4013     if ( diag
.hasError() || (leInfo
.chainedFixups 
== nullptr) ) 
4016     return (dyld_chained_fixups_header
*)getLinkEditContent(leInfo
.layout
, leInfo
.chainedFixups
->dataoff
); 
4019 uint16_t MachOAnalyzer::chainedPointerFormat(const dyld_chained_fixups_header
* header
) 
4021     const dyld_chained_starts_in_image
* startsInfo 
= (dyld_chained_starts_in_image
*)((uint8_t*)header 
+ header
->starts_offset
); 
4022     for (uint32_t i
=0; i 
< startsInfo
->seg_count
; ++i
) { 
4023         uint32_t segInfoOffset 
= startsInfo
->seg_info_offset
[i
]; 
4024         // 0 offset means this segment has no fixups 
4025         if ( segInfoOffset 
== 0 ) 
4027         const dyld_chained_starts_in_segment
* segInfo 
= (dyld_chained_starts_in_segment
*)((uint8_t*)startsInfo 
+ segInfoOffset
); 
4028         if ( segInfo
->page_count 
!= 0 ) 
4029             return segInfo
->pointer_format
; 
4031     return 0;  // no chains (perhaps no __DATA segment) 
4034 uint16_t MachOAnalyzer::chainedPointerFormat() const 
4036     const dyld_chained_fixups_header
* header 
= chainedFixupsHeader(); 
4037     if ( header 
!= nullptr ) { 
4038         // get pointer format from chain info struct in LINKEDIT 
4039         return chainedPointerFormat(header
); 
4041     assert(this->cputype 
== CPU_TYPE_ARM64 
&& (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E
) && "chainedPointerFormat() called on non-chained binary"); 
4042     return DYLD_CHAINED_PTR_ARM64E
; 
4045 #if (BUILDING_DYLD || BUILDING_LIBDYLD) && !__arm64e__ 
4046   #define SUPPORT_OLD_ARM64E_FORMAT 0 
4048   #define SUPPORT_OLD_ARM64E_FORMAT 1 
4051 // find dyld_chained_starts_in_image* in image 
4052 // if old arm64e binary, synthesize dyld_chained_starts_in_image* 
4053 void MachOAnalyzer::withChainStarts(Diagnostics
& diag
, uint64_t startsStructOffsetHint
, void (^callback
)(const dyld_chained_starts_in_image
*)) const 
4055     if ( startsStructOffsetHint 
!= 0 ) { 
4056         // we have a pre-computed offset into LINKEDIT for dyld_chained_starts_in_image 
4057         callback((dyld_chained_starts_in_image
*)((uint8_t*)this + startsStructOffsetHint
)); 
4061     LinkEditInfo leInfo
; 
4062     getLinkEditPointers(diag
, leInfo
); 
4063     if ( diag
.hasError() ) 
4066     if ( leInfo
.chainedFixups 
!= nullptr ) { 
4067         // find dyld_chained_starts_in_image from dyld_chained_fixups_header 
4068         const dyld_chained_fixups_header
* header 
= (dyld_chained_fixups_header
*)getLinkEditContent(leInfo
.layout
, leInfo
.chainedFixups
->dataoff
); 
4069         callback((dyld_chained_starts_in_image
*)((uint8_t*)header 
+ header
->starts_offset
)); 
4071 #if SUPPORT_OLD_ARM64E_FORMAT 
4072     // don't want this code in non-arm64e dyld because it causes a stack protector which dereferences a GOT pointer before GOT is set up 
4073     else if ( (leInfo
.dyldInfo 
!= nullptr) && (this->cputype 
== CPU_TYPE_ARM64
) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E
) ) { 
4074         // old arm64e binary, create a dyld_chained_starts_in_image for caller 
4075         uint64_t baseAddress 
= preferredLoadAddress(); 
4076         BLOCK_ACCCESSIBLE_ARRAY(uint8_t, buffer
, leInfo
.dyldInfo
->bind_size 
+ 512); 
4077         dyld_chained_starts_in_image
* header 
= (dyld_chained_starts_in_image
*)buffer
; 
4078         header
->seg_count 
= leInfo
.layout
.linkeditSegIndex
; 
4079         for (uint32_t i
=0; i 
< header
->seg_count
; ++i
) 
4080             header
->seg_info_offset
[i
] = 0; 
4081         __block 
uint8_t curSegIndex 
= 0; 
4082         __block dyld_chained_starts_in_segment
* curSeg 
= (dyld_chained_starts_in_segment
*)(&(header
->seg_info_offset
[header
->seg_count
])); 
4083         parseOrgArm64eChainedFixups(diag
, nullptr, nullptr, ^(const LinkEditInfo
& leInfo2
, const SegmentInfo segments
[], uint8_t segmentIndex
, 
4084                                                               bool segIndexSet
, uint64_t segmentOffset
, uint16_t format
, bool& stop
) { 
4085             uint32_t pageIndex 
= (uint32_t)(segmentOffset
/0x1000); 
4086             if ( segmentIndex 
!= curSegIndex 
) { 
4087                 if ( curSegIndex 
== 0 ) { 
4088                     header
->seg_info_offset
[segmentIndex
] = (uint32_t)((uint8_t*)curSeg 
- buffer
); 
4091                     header
->seg_info_offset
[segmentIndex
] = (uint32_t)((uint8_t*)(&curSeg
->page_start
[curSeg
->page_count
]) - buffer
); 
4092                     curSeg 
= (dyld_chained_starts_in_segment
*)((uint8_t*)header
+header
->seg_info_offset
[segmentIndex
]); 
4094                curSeg
->page_count 
= 0; 
4095                curSegIndex 
= segmentIndex
; 
4097             while ( curSeg
->page_count 
!= pageIndex 
) { 
4098                 curSeg
->page_start
[curSeg
->page_count
] = 0xFFFF; 
4099                 curSeg
->page_count
++; 
4101             curSeg
->size                  
= (uint32_t)((uint8_t*)(&curSeg
->page_start
[pageIndex
]) - (uint8_t*)curSeg
); 
4102             curSeg
->page_size             
= 0x1000; // old arm64e encoding used 4KB pages 
4103             curSeg
->pointer_format        
= DYLD_CHAINED_PTR_ARM64E
; 
4104             curSeg
->segment_offset        
= segments
[segmentIndex
].vmAddr 
- baseAddress
; 
4105             curSeg
->max_valid_pointer     
= 0; 
4106             curSeg
->page_count            
= pageIndex
+1; 
4107             curSeg
->page_start
[pageIndex
] = segmentOffset 
& 0xFFF; 
4108             //fprintf(stderr, "segment_offset=0x%llX, vmAddr=0x%llX\n", curSeg->segment_offset, segments[segmentIndex].vmAddr ); 
4109             //printf("segIndex=%d, segOffset=0x%08llX, page_start[%d]=0x%04X, page_start[%d]=0x%04X\n", 
4110             //        segmentIndex, segmentOffset, pageIndex, curSeg->page_start[pageIndex], pageIndex-1, pageIndex ? curSeg->page_start[pageIndex-1] : 0); 
4116         diag
.error("image does not use chained fixups"); 
4120 struct OldThreadsStartSection
 
4122     uint32_t        padding 
: 31, 
4124     uint32_t        chain_starts
[1]; 
4127 // ld64 can't sometimes determine the size of __thread_starts accurately, 
4128 // because these sections have to be given a size before everything is laid out, 
4129 // and you don't know the actual size of the chains until everything is 
4130 // laid out. In order to account for this, the linker puts trailing 0xFFFFFFFF at 
4131 // the end of the section, that must be ignored when walking the chains. This 
4132 // patch adjust the section size accordingly. 
4133 static uint32_t adjustStartsCount(uint32_t startsCount
, const uint32_t* starts
) { 
4134     for ( int i 
= startsCount
; i 
> 0; --i 
) 
4136         if ( starts
[i 
- 1] == 0xFFFFFFFF ) 
4144 bool MachOAnalyzer::hasFirmwareChainStarts(uint16_t* pointerFormat
, uint32_t* startsCount
, const uint32_t** starts
) const 
4146     if ( !this->isPreload() && !this->isStaticExecutable() ) 
4149     uint64_t sectionSize
; 
4150     if (const dyld_chained_starts_offsets
* sect 
= (dyld_chained_starts_offsets
*)this->findSectionContent("__TEXT", "__chain_starts", sectionSize
) ) { 
4151         *pointerFormat 
= sect
->pointer_format
; 
4152         *startsCount   
= sect
->starts_count
; 
4153         *starts        
= §
->chain_starts
[0]; 
4156     if (const OldThreadsStartSection
* sect 
= (OldThreadsStartSection
*)this->findSectionContent("__TEXT", "__thread_starts", sectionSize
) ) { 
4157         *pointerFormat 
= sect
->stride8 
? DYLD_CHAINED_PTR_ARM64E 
: DYLD_CHAINED_PTR_ARM64E_FIRMWARE
; 
4158         *startsCount   
= adjustStartsCount((uint32_t)(sectionSize
/4) - 1, sect
->chain_starts
); 
4159         *starts        
= sect
->chain_starts
; 
4166 MachOAnalyzer::ObjCInfo 
MachOAnalyzer::getObjCInfo() const 
4168     __block ObjCInfo result
; 
4169     result
.selRefCount      
= 0; 
4170     result
.classDefCount    
= 0; 
4171     result
.protocolDefCount 
= 0; 
4173     const uint32_t ptrSize  
= pointerSize(); 
4174     forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
4175         if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) == 0 ) { 
4176             if ( strcmp(sectInfo
.sectName
, "__objc_selrefs") == 0 ) 
4177                 result
.selRefCount 
+= (sectInfo
.sectSize
/ptrSize
); 
4178             else if ( strcmp(sectInfo
.sectName
, "__objc_classlist") == 0 ) 
4179                 result
.classDefCount 
+= (sectInfo
.sectSize
/ptrSize
); 
4180             else if ( strcmp(sectInfo
.sectName
, "__objc_protolist") == 0 ) 
4181                 result
.protocolDefCount 
+= (sectInfo
.sectSize
/ptrSize
); 
4183         else if ( (this->cputype 
== CPU_TYPE_I386
) && (strcmp(sectInfo
.segInfo
.segName
, "__OBJC") == 0) ) { 
4184             if ( strcmp(sectInfo
.sectName
, "__message_refs") == 0 ) 
4185                 result
.selRefCount 
+= (sectInfo
.sectSize
/4); 
4186             else if ( strcmp(sectInfo
.sectName
, "__class") == 0 ) 
4187                 result
.classDefCount 
+= (sectInfo
.sectSize
/48); 
4188             else if ( strcmp(sectInfo
.sectName
, "__protocol") == 0 ) 
4189                 result
.protocolDefCount 
+= (sectInfo
.sectSize
/20); 
4196 uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadOnlyDataField field
, uint32_t pointerSize
) const { 
4197     if (pointerSize 
== 8) { 
4198         typedef uint64_t PtrTy
; 
4201             uint32_t instanceStart
; 
4202             // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout 
4203             // on 64-bit archs, but no padding on 32-bit archs. 
4204             // This union is a way to model that. 
4206                 uint32_t   instanceSize
; 
4209             PtrTy ivarLayoutVMAddr
; 
4211             PtrTy baseMethodsVMAddr
; 
4212             PtrTy baseProtocolsVMAddr
; 
4214             PtrTy weakIvarLayoutVMAddr
; 
4215             PtrTy basePropertiesVMAddr
; 
4217         const class_ro_t
* classData 
= (const class_ro_t
*)(dataVMAddr 
+ vmAddrConverter
.slide
); 
4219         case ObjCClassInfo::ReadOnlyDataField::name
: 
4220             return vmAddrConverter
.convertToVMAddr(classData
->nameVMAddr
); 
4221         case ObjCClassInfo::ReadOnlyDataField::baseProtocols
: 
4222             return vmAddrConverter
.convertToVMAddr(classData
->baseProtocolsVMAddr
); 
4223         case ObjCClassInfo::ReadOnlyDataField::baseMethods
: 
4224             return vmAddrConverter
.convertToVMAddr(classData
->baseMethodsVMAddr
); 
4225         case ObjCClassInfo::ReadOnlyDataField::baseProperties
: 
4226             return vmAddrConverter
.convertToVMAddr(classData
->basePropertiesVMAddr
); 
4227         case ObjCClassInfo::ReadOnlyDataField::flags
: 
4228             return classData
->flags
; 
4231         typedef uint32_t PtrTy
; 
4234             uint32_t instanceStart
; 
4235             // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout 
4236             // on 64-bit archs, but no padding on 32-bit archs. 
4237             // This union is a way to model that. 
4239                 uint32_t   instanceSize
; 
4242             PtrTy ivarLayoutVMAddr
; 
4244             PtrTy baseMethodsVMAddr
; 
4245             PtrTy baseProtocolsVMAddr
; 
4247             PtrTy weakIvarLayoutVMAddr
; 
4248             PtrTy basePropertiesVMAddr
; 
4250         const class_ro_t
* classData 
= (const class_ro_t
*)(dataVMAddr 
+ vmAddrConverter
.slide
); 
4252             case ObjCClassInfo::ReadOnlyDataField::name
: 
4253                 return vmAddrConverter
.convertToVMAddr(classData
->nameVMAddr
); 
4254             case ObjCClassInfo::ReadOnlyDataField::baseProtocols
: 
4255                 return vmAddrConverter
.convertToVMAddr(classData
->baseProtocolsVMAddr
); 
4256             case ObjCClassInfo::ReadOnlyDataField::baseMethods
: 
4257                 return vmAddrConverter
.convertToVMAddr(classData
->baseMethodsVMAddr
); 
4258             case ObjCClassInfo::ReadOnlyDataField::baseProperties
: 
4259                 return vmAddrConverter
.convertToVMAddr(classData
->basePropertiesVMAddr
); 
4260             case ObjCClassInfo::ReadOnlyDataField::flags
: 
4261                 return classData
->flags
; 
4266 const char* MachOAnalyzer::getPrintableString(uint64_t stringVMAddr
, MachOAnalyzer::PrintableStringResult
& result
, 
4267                                               SectionCache
* sectionCache
, 
4268                                               bool (^sectionHandler
)(const SectionInfo
& sectionInfo
)) const { 
4269     if ( sectionCache 
!= nullptr ) { 
4270         // Make sure the string is pointing in to one of the supported sections 
4271         __block 
const dyld3::MachOAnalyzer::SectionInfo
* nameSectionInfo 
= nullptr; 
4272         for (const dyld3::MachOAnalyzer::SectionInfo
& sectionInfo 
: sectionCache
->sectionInfos
) { 
4273             if ( stringVMAddr 
< sectionInfo
.sectAddr 
) { 
4276             if ( stringVMAddr 
>= ( sectionInfo
.sectAddr 
+ sectionInfo
.sectSize
) ) { 
4279             nameSectionInfo 
= §ionInfo
; 
4283         if ( nameSectionInfo 
!= nullptr ) { 
4284             // The section handler may also reject this section 
4285             if ( sectionHandler 
!= nullptr ) { 
4286                 if (!sectionHandler(*nameSectionInfo
)) { 
4287                     result 
= PrintableStringResult::UnknownSection
; 
4292             result 
= PrintableStringResult::CanPrint
; 
4293             return (const char*)(stringVMAddr 
+ getSlide()); 
4297     // If the name isn't in the cache then find the section its in 
4299     uint32_t fairplayTextOffsetStart
; 
4300     uint32_t fairplayTextOffsetEnd
; 
4301     uint32_t fairplaySize
; 
4302     if ( isFairPlayEncrypted(fairplayTextOffsetStart
, fairplaySize
) ) { 
4303         fairplayTextOffsetEnd 
= fairplayTextOffsetStart 
+ fairplaySize
; 
4305         fairplayTextOffsetEnd 
= 0; 
4308     result 
= PrintableStringResult::UnknownSection
; 
4309     forEachSection(^(const MachOAnalyzer::SectionInfo 
§Info
, bool malformedSectionRange
, bool &stop
) { 
4310         if ( stringVMAddr 
< sectInfo
.sectAddr 
) { 
4313         if ( stringVMAddr 
>= ( sectInfo
.sectAddr 
+ sectInfo
.sectSize
) ) { 
4317         // We can't scan this section if its protected 
4318         if ( sectInfo
.segInfo
.isProtected 
) { 
4319             result 
= PrintableStringResult::ProtectedSection
; 
4324         // We can't scan this section if it overlaps with the fairplay range 
4325         if ( fairplayTextOffsetEnd 
< sectInfo
.sectFileOffset 
) { 
4326             // Fairplay range ends before section 
4327         } else if ( fairplayTextOffsetStart 
> (sectInfo
.sectFileOffset 
+ sectInfo
.sectSize
) ) { 
4328             // Fairplay range starts after section 
4331             result 
= PrintableStringResult::FairPlayEncrypted
; 
4336         // The section handler may also reject this section 
4337         if ( sectionHandler 
!= nullptr ) { 
4338             if (!sectionHandler(sectInfo
)) { 
4339                 result 
= PrintableStringResult::UnknownSection
; 
4344         // Cache this section for later. 
4345         if ( sectionCache 
!= nullptr ) { 
4346             sectionCache
->sectionInfos
.push_back(sectInfo
); 
4348         result 
= PrintableStringResult::CanPrint
; 
4352 #if BUILDING_SHARED_CACHE_UTIL || BUILDING_DYLDINFO 
4353     // The shared cache coalesces strings in to their own section. 
4354     // Assume its a valid pointer 
4355     if (result 
== PrintableStringResult::UnknownSection
) { 
4356         result 
= PrintableStringResult::CanPrint
; 
4357         return (const char*)(stringVMAddr 
+ getSlide()); 
4361     if (result 
== PrintableStringResult::CanPrint
) 
4362         return (const char*)(stringVMAddr 
+ getSlide()); 
4366 bool MachOAnalyzer::SectionCache::findSectionForVMAddr(uint64_t vmAddr
, bool (^sectionHandler
)(const SectionInfo
& sectionInfo
)) { 
4368     // Make sure the string is pointing in to one of the supported sections 
4369     __block 
const dyld3::MachOAnalyzer::SectionInfo
* foundSectionInfo 
= nullptr; 
4370     for (const dyld3::MachOAnalyzer::SectionInfo
& sectionInfo 
: sectionInfos
) { 
4371         if ( vmAddr 
< sectionInfo
.sectAddr 
) { 
4374         if ( vmAddr 
>= ( sectionInfo
.sectAddr 
+ sectionInfo
.sectSize
) ) { 
4377         foundSectionInfo 
= §ionInfo
; 
4381     if ( foundSectionInfo 
!= nullptr ) { 
4382         // The section handler may also reject this section 
4383         if ( sectionHandler 
!= nullptr ) { 
4384             if (!sectionHandler(*foundSectionInfo
)) { 
4389         // Found a section, so return true 
4393     // If the name isn't in the cache then find the section its in 
4395     uint32_t fairplayTextOffsetStart
; 
4396     uint32_t fairplayTextOffsetEnd
; 
4397     uint32_t fairplaySize
; 
4398     if ( ma
->isFairPlayEncrypted(fairplayTextOffsetStart
, fairplaySize
) ) { 
4399         fairplayTextOffsetEnd 
= fairplayTextOffsetStart 
+ fairplaySize
; 
4401         fairplayTextOffsetEnd 
= 0; 
4404     __block 
bool foundValidSection 
= false; 
4405     ma
->forEachSection(^(const MachOAnalyzer::SectionInfo 
§Info
, bool malformedSectionRange
, bool &stop
) { 
4406         if ( vmAddr 
< sectInfo
.sectAddr 
) { 
4409         if ( vmAddr 
>= ( sectInfo
.sectAddr 
+ sectInfo
.sectSize
) ) { 
4413         // We can't scan this section if it overlaps with the fairplay range 
4414         if ( fairplayTextOffsetEnd 
< sectInfo
.sectFileOffset 
) { 
4415             // Fairplay range ends before section 
4416         } else if ( fairplayTextOffsetStart 
> (sectInfo
.sectFileOffset 
+ sectInfo
.sectSize
) ) { 
4417             // Fairplay range starts after section 
4424         // The section handler may also reject this section 
4425         if ( sectionHandler 
!= nullptr ) { 
4426             if (!sectionHandler(sectInfo
)) { 
4431         // Cache this section for later. 
4432         sectionInfos
.push_back(sectInfo
); 
4433         foundValidSection 
= true; 
4437     return foundValidSection
; 
4440 void MachOAnalyzer::forEachObjCClass(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, 
4441                                      void (^handler
)(Diagnostics
& diag
, uint64_t classVMAddr
, 
4442                                                      uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, 
4443                                                      const ObjCClassInfo
& objcClass
, bool isMetaClass
)) const { 
4444     const uint64_t ptrSize 
= pointerSize(); 
4445     intptr_t slide 
= getSlide(); 
4447     forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
4448         if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 ) 
4450         if ( strcmp(sectInfo
.sectName
, "__objc_classlist") != 0 ) 
4452         const uint8_t*  classList       
= (uint8_t*)(sectInfo
.sectAddr 
+ slide
); 
4453         uint64_t        classListSize   
= sectInfo
.sectSize
; 
4455         if ( (classListSize 
% ptrSize
) != 0 ) { 
4456             diag
.error("Invalid objc class section size"); 
4460         if ( ptrSize 
== 8 ) { 
4461             typedef uint64_t PtrTy
; 
4463             for (uint64_t i 
= 0; i 
!= classListSize
; i 
+= sizeof(PtrTy
)) { 
4464                 uint64_t classVMAddr 
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(classList 
+ i
)); 
4465                 parseObjCClass(diag
, vmAddrConverter
, classVMAddr
, ^(Diagnostics
& classDiag
, uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, const ObjCClassInfo
& objcClass
) { 
4466                     handler(classDiag
, classVMAddr
, classSuperclassVMAddr
, classDataVMAddr
, objcClass
, false); 
4467                     if (classDiag
.hasError()) 
4470                     // Then parse and call for the metaclass 
4471                     uint64_t isaVMAddr 
= objcClass
.isaVMAddr
; 
4472                     parseObjCClass(classDiag
, vmAddrConverter
, isaVMAddr
, ^(Diagnostics
& metaclassDiag
, uint64_t metaclassSuperclassVMAddr
, uint64_t metaclassDataVMAddr
, const ObjCClassInfo
& objcMetaclass
) { 
4473                         handler(metaclassDiag
, isaVMAddr
, metaclassSuperclassVMAddr
, metaclassDataVMAddr
, objcMetaclass
, true); 
4476                 if (diag
.hasError()) 
4480             typedef uint32_t PtrTy
; 
4482             for (uint64_t i 
= 0; i 
!= classListSize
; i 
+= sizeof(PtrTy
)) { 
4483                 uint64_t classVMAddr 
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(classList 
+ i
)); 
4484                 parseObjCClass(diag
, vmAddrConverter
, classVMAddr
, ^(Diagnostics
& classDiag
, uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, const ObjCClassInfo
& objcClass
) { 
4485                     handler(classDiag
, classVMAddr
, classSuperclassVMAddr
, classDataVMAddr
, objcClass
, false); 
4486                     if (classDiag
.hasError()) 
4489                     // Then parse and call for the metaclass 
4490                     uint64_t isaVMAddr 
= objcClass
.isaVMAddr
; 
4491                     parseObjCClass(classDiag
, vmAddrConverter
, isaVMAddr
, ^(Diagnostics
& metaclassDiag
, uint64_t metaclassSuperclassVMAddr
, uint64_t metaclassDataVMAddr
, const ObjCClassInfo
& objcMetaclass
) { 
4492                         handler(metaclassDiag
, isaVMAddr
, metaclassSuperclassVMAddr
, metaclassDataVMAddr
, objcMetaclass
, true); 
4495                 if (diag
.hasError()) 
4502 void MachOAnalyzer::parseObjCClass(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, 
4503                                    uint64_t classVMAddr
, 
4504                                    void (^handler
)(Diagnostics
& diag
, 
4505                                                    uint64_t classSuperclassVMAddr
, 
4506                                                    uint64_t classDataVMAddr
, 
4507                                                    const ObjCClassInfo
& objcClass
)) const { 
4508     const uint64_t ptrSize 
= pointerSize(); 
4509     intptr_t slide 
= getSlide(); 
4511     uint64_t classSuperclassVMAddr 
= 0; 
4512     uint64_t classDataVMAddr       
= 0; 
4513     ObjCClassInfo objcClass
; 
4515     if ( ptrSize 
== 8 ) { 
4516        struct objc_class_t 
{ 
4518            uint64_t superclassVMAddr
; 
4519            uint64_t methodCacheBuckets
; 
4520            uint64_t methodCacheProperties
; 
4521            uint64_t dataVMAddrAndFastFlags
; 
4523         // This matches "struct TargetClassMetadata" from Metadata.h in Swift 
4524         struct swift_class_metadata_t 
: objc_class_t 
{ 
4525             uint32_t swiftClassFlags
; 
4528             FAST_DATA_MASK 
= 0x00007ffffffffff8ULL
 
4530         classSuperclassVMAddr 
= classVMAddr 
+ offsetof(objc_class_t
, superclassVMAddr
); 
4531         classDataVMAddr       
= classVMAddr 
+ offsetof(objc_class_t
, dataVMAddrAndFastFlags
); 
4533         // First call the handler on the class 
4534         const objc_class_t
*           classPtr      
= (const objc_class_t
*)(classVMAddr 
+ slide
); 
4535         const swift_class_metadata_t
* swiftClassPtr 
= (const swift_class_metadata_t
*)classPtr
; 
4536         objcClass
.isaVMAddr         
= vmAddrConverter
.convertToVMAddr(classPtr
->isaVMAddr
); 
4537         objcClass
.superclassVMAddr  
= vmAddrConverter
.convertToVMAddr(classPtr
->superclassVMAddr
); 
4538         objcClass
.methodCacheVMAddr  
= classPtr
->methodCacheProperties 
== 0 ? 0 : vmAddrConverter
.convertToVMAddr(classPtr
->methodCacheProperties
); 
4539         objcClass
.dataVMAddr        
= vmAddrConverter
.convertToVMAddr(classPtr
->dataVMAddrAndFastFlags
) & FAST_DATA_MASK
; 
4540         objcClass
.vmAddrConverter   
= vmAddrConverter
; 
4541         objcClass
.isSwiftLegacy     
= classPtr
->dataVMAddrAndFastFlags 
& ObjCClassInfo::FAST_IS_SWIFT_LEGACY
; 
4542         objcClass
.isSwiftStable     
= classPtr
->dataVMAddrAndFastFlags 
& ObjCClassInfo::FAST_IS_SWIFT_STABLE
; 
4543         // The Swift class flags are only present if the class is swift 
4544         objcClass
.swiftClassFlags   
= (objcClass
.isSwiftLegacy 
|| objcClass
.isSwiftStable
) ? swiftClassPtr
->swiftClassFlags 
: 0; 
4546         struct objc_class_t 
{ 
4548             uint32_t superclassVMAddr
; 
4549             uint32_t methodCacheBuckets
; 
4550             uint32_t methodCacheProperties
; 
4551             uint32_t dataVMAddrAndFastFlags
; 
4553         // This matches "struct TargetClassMetadata" from Metadata.h in Swift 
4554         struct swift_class_metadata_t 
: objc_class_t 
{ 
4555             uint32_t swiftClassFlags
; 
4558             FAST_DATA_MASK 
= 0xfffffffcUL
 
4560         classSuperclassVMAddr 
= classVMAddr 
+ offsetof(objc_class_t
, superclassVMAddr
); 
4561         classDataVMAddr       
= classVMAddr 
+ offsetof(objc_class_t
, dataVMAddrAndFastFlags
); 
4563         // First call the handler on the class 
4564         const objc_class_t
*           classPtr      
= (const objc_class_t
*)(classVMAddr 
+ slide
); 
4565         const swift_class_metadata_t
* swiftClassPtr 
= (const swift_class_metadata_t
*)classPtr
; 
4566         objcClass
.isaVMAddr         
= vmAddrConverter
.convertToVMAddr(classPtr
->isaVMAddr
); 
4567         objcClass
.superclassVMAddr  
= vmAddrConverter
.convertToVMAddr(classPtr
->superclassVMAddr
); 
4568         objcClass
.methodCacheVMAddr  
= classPtr
->methodCacheProperties 
== 0 ? 0 : vmAddrConverter
.convertToVMAddr(classPtr
->methodCacheProperties
); 
4569         objcClass
.dataVMAddr        
= vmAddrConverter
.convertToVMAddr(classPtr
->dataVMAddrAndFastFlags
) & FAST_DATA_MASK
; 
4570         objcClass
.vmAddrConverter   
= vmAddrConverter
; 
4571         objcClass
.isSwiftLegacy     
= classPtr
->dataVMAddrAndFastFlags 
& ObjCClassInfo::FAST_IS_SWIFT_LEGACY
; 
4572         objcClass
.isSwiftStable     
= classPtr
->dataVMAddrAndFastFlags 
& ObjCClassInfo::FAST_IS_SWIFT_STABLE
; 
4573         // The Swift class flags are only present if the class is swift 
4574         objcClass
.swiftClassFlags   
= (objcClass
.isSwiftLegacy 
|| objcClass
.isSwiftStable
) ? swiftClassPtr
->swiftClassFlags 
: 0; 
4577     handler(diag
, classSuperclassVMAddr
, classDataVMAddr
, objcClass
); 
4580 void MachOAnalyzer::forEachObjCCategory(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, 
4581                                         void (^handler
)(Diagnostics
& diag
, uint64_t categoryVMAddr
, 
4582                                                         const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
)) const { 
4583     const uint64_t ptrSize 
= pointerSize(); 
4584     intptr_t slide 
= getSlide(); 
4586     forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
4587         if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 ) 
4589         if ( strcmp(sectInfo
.sectName
, "__objc_catlist") != 0 ) 
4591         const uint8_t*  categoryList       
= (uint8_t*)(sectInfo
.sectAddr 
+ slide
); 
4592         uint64_t        categoryListSize   
= sectInfo
.sectSize
; 
4594         if ( (categoryListSize 
% ptrSize
) != 0 ) { 
4595             diag
.error("Invalid objc category section size"); 
4599         if ( ptrSize 
== 8 ) { 
4600             typedef uint64_t PtrTy
; 
4601             struct objc_category_t 
{ 
4604                 PtrTy instanceMethodsVMAddr
; 
4605                 PtrTy classMethodsVMAddr
; 
4606                 PtrTy protocolsVMAddr
; 
4607                 PtrTy instancePropertiesVMAddr
; 
4609             for (uint64_t i 
= 0; i 
!= categoryListSize
; i 
+= sizeof(PtrTy
)) { 
4610                 uint64_t categoryVMAddr 
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(categoryList 
+ i
)); 
4612                 const objc_category_t
* categoryPtr 
= (const objc_category_t
*)(categoryVMAddr 
+ slide
); 
4613                 ObjCCategory objCCategory
; 
4614                 objCCategory
.nameVMAddr                 
= vmAddrConverter
.convertToVMAddr(categoryPtr
->nameVMAddr
); 
4615                 objCCategory
.clsVMAddr                  
= vmAddrConverter
.convertToVMAddr(categoryPtr
->clsVMAddr
); 
4616                 objCCategory
.instanceMethodsVMAddr      
= vmAddrConverter
.convertToVMAddr(categoryPtr
->instanceMethodsVMAddr
); 
4617                 objCCategory
.classMethodsVMAddr         
= vmAddrConverter
.convertToVMAddr(categoryPtr
->classMethodsVMAddr
); 
4618                 objCCategory
.protocolsVMAddr            
= vmAddrConverter
.convertToVMAddr(categoryPtr
->protocolsVMAddr
); 
4619                 objCCategory
.instancePropertiesVMAddr   
= vmAddrConverter
.convertToVMAddr(categoryPtr
->instancePropertiesVMAddr
); 
4620                 handler(diag
, categoryVMAddr
, objCCategory
); 
4621                 if (diag
.hasError()) 
4625             typedef uint32_t PtrTy
; 
4626             struct objc_category_t 
{ 
4629                 PtrTy instanceMethodsVMAddr
; 
4630                 PtrTy classMethodsVMAddr
; 
4631                 PtrTy protocolsVMAddr
; 
4632                 PtrTy instancePropertiesVMAddr
; 
4634             for (uint64_t i 
= 0; i 
!= categoryListSize
; i 
+= sizeof(PtrTy
)) { 
4635                 uint64_t categoryVMAddr 
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(categoryList 
+ i
)); 
4637                 const objc_category_t
* categoryPtr 
= (const objc_category_t
*)(categoryVMAddr 
+ slide
); 
4638                 ObjCCategory objCCategory
; 
4639                 objCCategory
.nameVMAddr                 
= vmAddrConverter
.convertToVMAddr(categoryPtr
->nameVMAddr
); 
4640                 objCCategory
.clsVMAddr                  
= vmAddrConverter
.convertToVMAddr(categoryPtr
->clsVMAddr
); 
4641                 objCCategory
.instanceMethodsVMAddr      
= vmAddrConverter
.convertToVMAddr(categoryPtr
->instanceMethodsVMAddr
); 
4642                 objCCategory
.classMethodsVMAddr         
= vmAddrConverter
.convertToVMAddr(categoryPtr
->classMethodsVMAddr
); 
4643                 objCCategory
.protocolsVMAddr            
= vmAddrConverter
.convertToVMAddr(categoryPtr
->protocolsVMAddr
); 
4644                 objCCategory
.instancePropertiesVMAddr   
= vmAddrConverter
.convertToVMAddr(categoryPtr
->instancePropertiesVMAddr
); 
4645                 handler(diag
, categoryVMAddr
, objCCategory
); 
4646                 if (diag
.hasError()) 
4653 void MachOAnalyzer::forEachObjCProtocol(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, 
4654                                         void (^handler
)(Diagnostics
& diag
, uint64_t categoryVMAddr
, 
4655                                                         const dyld3::MachOAnalyzer::ObjCProtocol
& objCProtocol
)) const { 
4656     const uint64_t ptrSize 
= pointerSize(); 
4657     intptr_t slide 
= getSlide(); 
4659     forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
4660         if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 ) 
4662         if ( strcmp(sectInfo
.sectName
, "__objc_protolist") != 0 ) 
4664         const uint8_t*  protocolList       
= (uint8_t*)(sectInfo
.sectAddr 
+ slide
); 
4665         uint64_t        protocolListSize   
= sectInfo
.sectSize
; 
4667         if ( (protocolListSize 
% ptrSize
) != 0 ) { 
4668             diag
.error("Invalid objc protocol section size"); 
4672         if ( ptrSize 
== 8 ) { 
4673             typedef uint64_t PtrTy
; 
4677                 PtrTy    protocolsVMAddr
; 
4678                 PtrTy    instanceMethodsVMAddr
; 
4679                 PtrTy    classMethodsVMAddr
; 
4680                 PtrTy    optionalInstanceMethodsVMAddr
; 
4681                 PtrTy    optionalClassMethodsVMAddr
; 
4682                 PtrTy    instancePropertiesVMAddr
; 
4685                 // Fields below this point are not always present on disk. 
4686                 PtrTy    extendedMethodTypesVMAddr
; 
4687                 PtrTy    demangledNameVMAddr
; 
4688                 PtrTy    classPropertiesVMAddr
; 
4690             for (uint64_t i 
= 0; i 
!= protocolListSize
; i 
+= sizeof(PtrTy
)) { 
4691                 uint64_t protocolVMAddr 
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(protocolList 
+ i
)); 
4693                 const protocol_t
* protocolPtr 
= (const protocol_t
*)(protocolVMAddr 
+ slide
); 
4694                 ObjCProtocol objCProtocol
; 
4695                 objCProtocol
.isaVMAddr                          
= vmAddrConverter
.convertToVMAddr(protocolPtr
->isaVMAddr
); 
4696                 objCProtocol
.nameVMAddr                         
= vmAddrConverter
.convertToVMAddr(protocolPtr
->nameVMAddr
); 
4697                 objCProtocol
.protocolsVMAddr                    
= vmAddrConverter
.convertToVMAddr(protocolPtr
->protocolsVMAddr
); 
4698                 objCProtocol
.instanceMethodsVMAddr              
= vmAddrConverter
.convertToVMAddr(protocolPtr
->instanceMethodsVMAddr
); 
4699                 objCProtocol
.classMethodsVMAddr                 
= vmAddrConverter
.convertToVMAddr(protocolPtr
->classMethodsVMAddr
); 
4700                 objCProtocol
.optionalInstanceMethodsVMAddr      
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalInstanceMethodsVMAddr
); 
4701                 objCProtocol
.optionalClassMethodsVMAddr         
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalClassMethodsVMAddr
); 
4703                 handler(diag
, protocolVMAddr
, objCProtocol
); 
4704                 if (diag
.hasError()) 
4708             typedef uint32_t PtrTy
; 
4712                 PtrTy    protocolsVMAddr
; 
4713                 PtrTy    instanceMethodsVMAddr
; 
4714                 PtrTy    classMethodsVMAddr
; 
4715                 PtrTy    optionalInstanceMethodsVMAddr
; 
4716                 PtrTy    optionalClassMethodsVMAddr
; 
4717                 PtrTy    instancePropertiesVMAddr
; 
4720                 // Fields below this point are not always present on disk. 
4721                 PtrTy    extendedMethodTypesVMAddr
; 
4722                 PtrTy    demangledNameVMAddr
; 
4723                 PtrTy    classPropertiesVMAddr
; 
4725             for (uint64_t i 
= 0; i 
!= protocolListSize
; i 
+= sizeof(PtrTy
)) { 
4726                 uint64_t protocolVMAddr 
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(protocolList 
+ i
)); 
4728                 const protocol_t
* protocolPtr 
= (const protocol_t
*)(protocolVMAddr 
+ slide
); 
4729                 ObjCProtocol objCProtocol
; 
4730                 objCProtocol
.isaVMAddr                          
= vmAddrConverter
.convertToVMAddr(protocolPtr
->isaVMAddr
); 
4731                 objCProtocol
.nameVMAddr                         
= vmAddrConverter
.convertToVMAddr(protocolPtr
->nameVMAddr
); 
4732                 objCProtocol
.protocolsVMAddr                    
= vmAddrConverter
.convertToVMAddr(protocolPtr
->protocolsVMAddr
); 
4733                 objCProtocol
.instanceMethodsVMAddr              
= vmAddrConverter
.convertToVMAddr(protocolPtr
->instanceMethodsVMAddr
); 
4734                 objCProtocol
.classMethodsVMAddr                 
= vmAddrConverter
.convertToVMAddr(protocolPtr
->classMethodsVMAddr
); 
4735                 objCProtocol
.optionalInstanceMethodsVMAddr      
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalInstanceMethodsVMAddr
); 
4736                 objCProtocol
.optionalClassMethodsVMAddr         
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalClassMethodsVMAddr
); 
4738                 handler(diag
, protocolVMAddr
, objCProtocol
); 
4739                 if (diag
.hasError()) 
4746 void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr
, const VMAddrConverter
& vmAddrConverter
, 
4747                                       void (^handler
)(uint64_t methodVMAddr
, const ObjCMethod
& method
), 
4748                                       bool* isRelativeMethodList
) const { 
4749     if ( methodListVMAddr 
== 0 ) 
4752     const uint64_t ptrSize 
= pointerSize(); 
4753     intptr_t slide 
= getSlide(); 
4755     if ( ptrSize 
== 8 ) { 
4756         typedef uint64_t PtrTy
; 
4757         struct method_list_t 
{ 
4760             PtrTy       methodArrayBase
; // Note this is the start the array method_t[0] 
4762             uint32_t getEntsize() const { 
4763                 return entsize 
& ObjCMethodList::methodListSizeMask
; 
4766             bool usesDirectOffsetsToSelectors() const { 
4767                 return (entsize 
& 0x40000000) != 0; 
4770             bool usesRelativeOffsets() const { 
4771                 return (entsize 
& 0x80000000) != 0; 
4776             PtrTy nameVMAddr
;   // SEL 
4777             PtrTy typesVMAddr
;  // const char * 
4778             PtrTy impVMAddr
;    // IMP 
4781         struct relative_method_t 
{ 
4782             int32_t nameOffset
;   // SEL* 
4783             int32_t typesOffset
;  // const char * 
4784             int32_t impOffset
;    // IMP 
4787         const method_list_t
* methodList 
= (const method_list_t
*)(methodListVMAddr 
+ slide
); 
4788         if ( methodList 
== nullptr ) 
4790         bool relativeMethodListsAreOffsetsToSelectors 
= methodList
->usesDirectOffsetsToSelectors(); 
4791         uint64_t methodListArrayBaseVMAddr 
= methodListVMAddr 
+ offsetof(method_list_t
, methodArrayBase
); 
4792         for (unsigned i 
= 0; i 
!= methodList
->count
; ++i
) { 
4793             uint64_t methodEntryOffset 
= i 
* methodList
->getEntsize(); 
4794             uint64_t methodVMAddr 
= methodListArrayBaseVMAddr 
+ methodEntryOffset
; 
4796             if ( methodList
->usesRelativeOffsets() ) { 
4797                 const relative_method_t
* methodPtr 
= (const relative_method_t
*)(methodVMAddr 
+ slide
); 
4798                 if ( relativeMethodListsAreOffsetsToSelectors 
) { 
4799                     method
.nameVMAddr  
= methodVMAddr 
+ offsetof(relative_method_t
, nameOffset
) + methodPtr
->nameOffset
; 
4801                     PtrTy
* nameLocation 
= (PtrTy
*)((uint8_t*)&methodPtr
->nameOffset 
+ methodPtr
->nameOffset
); 
4802                     method
.nameVMAddr   
= vmAddrConverter
.convertToVMAddr(*nameLocation
); 
4804                 method
.typesVMAddr  
= methodVMAddr 
+ offsetof(relative_method_t
, typesOffset
) + methodPtr
->typesOffset
; 
4805                 method
.impVMAddr    
= methodVMAddr 
+ offsetof(relative_method_t
, impOffset
) + methodPtr
->impOffset
; 
4806                 method
.nameLocationVMAddr 
= methodVMAddr 
+ offsetof(relative_method_t
, nameOffset
) + methodPtr
->nameOffset
; 
4808                 const method_t
* methodPtr 
= (const method_t
*)(methodVMAddr 
+ slide
); 
4809                 method
.nameVMAddr   
= vmAddrConverter
.convertToVMAddr(methodPtr
->nameVMAddr
); 
4810                 method
.typesVMAddr  
= vmAddrConverter
.convertToVMAddr(methodPtr
->typesVMAddr
); 
4811                 method
.impVMAddr    
= vmAddrConverter
.convertToVMAddr(methodPtr
->impVMAddr
); 
4812                 method
.nameLocationVMAddr 
= methodVMAddr 
+ offsetof(method_t
, nameVMAddr
); 
4814             handler(methodVMAddr
, method
); 
4817         if ( isRelativeMethodList 
!= nullptr ) 
4818             *isRelativeMethodList 
= methodList
->usesRelativeOffsets(); 
4820         typedef uint32_t PtrTy
; 
4821         struct method_list_t 
{ 
4824             PtrTy       methodArrayBase
; // Note this is the start the array method_t[0] 
4826             uint32_t getEntsize() const { 
4827                 return entsize 
& ObjCMethodList::methodListSizeMask
; 
4830             bool usesDirectOffsetsToSelectors() const { 
4831                 return (entsize 
& 0x40000000) != 0; 
4834             bool usesRelativeOffsets() const { 
4835                 return (entsize 
& 0x80000000) != 0; 
4840             PtrTy nameVMAddr
;   // SEL 
4841             PtrTy typesVMAddr
;  // const char * 
4842             PtrTy impVMAddr
;    // IMP 
4845         struct relative_method_t 
{ 
4846             int32_t nameOffset
;   // SEL* 
4847             int32_t typesOffset
;  // const char * 
4848             int32_t impOffset
;    // IMP 
4851         const method_list_t
* methodList 
= (const method_list_t
*)(methodListVMAddr 
+ slide
); 
4852         if ( methodList 
== nullptr ) 
4854         bool relativeMethodListsAreOffsetsToSelectors 
= methodList
->usesDirectOffsetsToSelectors(); 
4855         uint64_t methodListArrayBaseVMAddr 
= methodListVMAddr 
+ offsetof(method_list_t
, methodArrayBase
); 
4856         for (unsigned i 
= 0; i 
!= methodList
->count
; ++i
) { 
4857             uint64_t methodEntryOffset 
= i 
* methodList
->getEntsize(); 
4858             uint64_t methodVMAddr 
= methodListArrayBaseVMAddr 
+ methodEntryOffset
; 
4860             if ( methodList
->usesRelativeOffsets() ) { 
4861                 const relative_method_t
* methodPtr 
= (const relative_method_t
*)(methodVMAddr 
+ slide
); 
4862                 if ( relativeMethodListsAreOffsetsToSelectors 
) { 
4863                     method
.nameVMAddr  
= methodVMAddr 
+ offsetof(relative_method_t
, nameOffset
) + methodPtr
->nameOffset
; 
4865                     PtrTy
* nameLocation 
= (PtrTy
*)((uint8_t*)&methodPtr
->nameOffset 
+ methodPtr
->nameOffset
); 
4866                     method
.nameVMAddr   
= vmAddrConverter
.convertToVMAddr(*nameLocation
); 
4868                 method
.typesVMAddr  
= methodVMAddr 
+ offsetof(relative_method_t
, typesOffset
) + methodPtr
->typesOffset
; 
4869                 method
.impVMAddr    
= methodVMAddr 
+ offsetof(relative_method_t
, impOffset
) + methodPtr
->impOffset
; 
4870                 method
.nameLocationVMAddr 
= methodVMAddr 
+ offsetof(relative_method_t
, nameOffset
) + methodPtr
->nameOffset
; 
4872                 const method_t
* methodPtr 
= (const method_t
*)(methodVMAddr 
+ slide
); 
4873                 method
.nameVMAddr   
= vmAddrConverter
.convertToVMAddr(methodPtr
->nameVMAddr
); 
4874                 method
.typesVMAddr  
= vmAddrConverter
.convertToVMAddr(methodPtr
->typesVMAddr
); 
4875                 method
.impVMAddr    
= vmAddrConverter
.convertToVMAddr(methodPtr
->impVMAddr
); 
4876                 method
.nameLocationVMAddr 
= methodVMAddr 
+ offsetof(method_t
, nameVMAddr
); 
4878             handler(methodVMAddr
, method
); 
4881         if ( isRelativeMethodList 
!= nullptr ) 
4882             *isRelativeMethodList 
= methodList
->usesRelativeOffsets(); 
4886 void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr
, const VMAddrConverter
& vmAddrConverter
, 
4887                                         void (^handler
)(uint64_t propertyVMAddr
, const ObjCProperty
& property
)) const { 
4888     if ( propertyListVMAddr 
== 0 ) 
4891     const uint64_t ptrSize 
= pointerSize(); 
4892     intptr_t slide 
= getSlide(); 
4894     if ( ptrSize 
== 8 ) { 
4895         typedef uint64_t PtrTy
; 
4896         struct property_list_t 
{ 
4899             PtrTy       propertyArrayBase
; // Note this is the start the array property_t[0] 
4901             uint32_t getEntsize() const { 
4902                 return (entsize
) & ~(uint32_t)3; 
4907             PtrTy nameVMAddr
;   // SEL 
4908             PtrTy attributesVMAddr
;  // const char * 
4911         const property_list_t
* propertyList 
= (const property_list_t
*)(propertyListVMAddr 
+ slide
); 
4912         uint64_t propertyListArrayBaseVMAddr 
= propertyListVMAddr 
+ offsetof(property_list_t
, propertyArrayBase
); 
4913         for (unsigned i 
= 0; i 
!= propertyList
->count
; ++i
) { 
4914             uint64_t propertyEntryOffset 
= i 
* propertyList
->getEntsize(); 
4915             uint64_t propertyVMAddr 
= propertyListArrayBaseVMAddr 
+ propertyEntryOffset
; 
4916             const property_t
* propertyPtr 
= (const property_t
*)(propertyVMAddr 
+ slide
); 
4917             ObjCProperty property
; 
4918             property
.nameVMAddr         
= vmAddrConverter
.convertToVMAddr(propertyPtr
->nameVMAddr
); 
4919             property
.attributesVMAddr   
= vmAddrConverter
.convertToVMAddr(propertyPtr
->attributesVMAddr
); 
4920             handler(propertyVMAddr
, property
); 
4923         typedef uint32_t PtrTy
; 
4924         struct property_list_t 
{ 
4927             PtrTy       propertyArrayBase
; // Note this is the start the array property_t[0] 
4929             uint32_t getEntsize() const { 
4930                 return (entsize
) & ~(uint32_t)3; 
4935             PtrTy nameVMAddr
;   // SEL 
4936             PtrTy attributesVMAddr
;  // const char * 
4939         const property_list_t
* propertyList 
= (const property_list_t
*)(propertyListVMAddr 
+ slide
); 
4940         uint64_t propertyListArrayBaseVMAddr 
= propertyListVMAddr 
+ offsetof(property_list_t
, propertyArrayBase
); 
4941         for (unsigned i 
= 0; i 
!= propertyList
->count
; ++i
) { 
4942             uint64_t propertyEntryOffset 
= i 
* propertyList
->getEntsize(); 
4943             uint64_t propertyVMAddr 
= propertyListArrayBaseVMAddr 
+ propertyEntryOffset
; 
4944             const property_t
* propertyPtr 
= (const property_t
*)(propertyVMAddr 
+ slide
); 
4945             ObjCProperty property
; 
4946             property
.nameVMAddr         
= vmAddrConverter
.convertToVMAddr(propertyPtr
->nameVMAddr
); 
4947             property
.attributesVMAddr   
= vmAddrConverter
.convertToVMAddr(propertyPtr
->attributesVMAddr
); 
4948             handler(propertyVMAddr
, property
); 
4953 void MachOAnalyzer::forEachObjCProtocol(uint64_t protocolListVMAddr
, const VMAddrConverter
& vmAddrConverter
, 
4954                                         void (^handler
)(uint64_t protocolRefVMAddr
, const ObjCProtocol
&)) const 
4956     if ( protocolListVMAddr 
== 0 ) 
4959     auto ptrSize 
= pointerSize(); 
4960     intptr_t slide 
= getSlide(); 
4962     if ( ptrSize 
== 8 ) { 
4963         typedef uint64_t PtrTy
; 
4964         struct protocol_ref_t 
{ 
4967         struct protocol_list_t 
{ 
4969             protocol_ref_t  array
[]; 
4974             PtrTy    protocolsVMAddr
; 
4975             PtrTy    instanceMethodsVMAddr
; 
4976             PtrTy    classMethodsVMAddr
; 
4977             PtrTy    optionalInstanceMethodsVMAddr
; 
4978             PtrTy    optionalClassMethodsVMAddr
; 
4979             PtrTy    instancePropertiesVMAddr
; 
4982             // Fields below this point are not always present on disk. 
4983             PtrTy    extendedMethodTypesVMAddr
; 
4984             PtrTy    demangledNameVMAddr
; 
4985             PtrTy    classPropertiesVMAddr
; 
4988         const protocol_list_t
* protoList 
= (const protocol_list_t
*)(protocolListVMAddr 
+ slide
); 
4989         for (PtrTy i 
= 0; i 
!= protoList
->count
; ++i
) { 
4990             uint64_t protocolVMAddr 
= vmAddrConverter
.convertToVMAddr(protoList
->array
[i
].refVMAddr
); 
4992             const protocol_t
* protocolPtr 
= (const protocol_t
*)(protocolVMAddr 
+ slide
); 
4993             ObjCProtocol objCProtocol
; 
4994             objCProtocol
.isaVMAddr                          
= vmAddrConverter
.convertToVMAddr(protocolPtr
->isaVMAddr
); 
4995             objCProtocol
.nameVMAddr                         
= vmAddrConverter
.convertToVMAddr(protocolPtr
->nameVMAddr
); 
4996             objCProtocol
.protocolsVMAddr                    
= vmAddrConverter
.convertToVMAddr(protocolPtr
->protocolsVMAddr
); 
4997             objCProtocol
.instanceMethodsVMAddr              
= vmAddrConverter
.convertToVMAddr(protocolPtr
->instanceMethodsVMAddr
); 
4998             objCProtocol
.classMethodsVMAddr                 
= vmAddrConverter
.convertToVMAddr(protocolPtr
->classMethodsVMAddr
); 
4999             objCProtocol
.optionalInstanceMethodsVMAddr      
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalInstanceMethodsVMAddr
); 
5000             objCProtocol
.optionalClassMethodsVMAddr         
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalClassMethodsVMAddr
); 
5002             handler(protocolVMAddr
, objCProtocol
); 
5005         typedef uint32_t PtrTy
; 
5006         struct protocol_ref_t 
{ 
5009         struct protocol_list_t 
{ 
5011             protocol_ref_t  array
[]; 
5016             PtrTy    protocolsVMAddr
; 
5017             PtrTy    instanceMethodsVMAddr
; 
5018             PtrTy    classMethodsVMAddr
; 
5019             PtrTy    optionalInstanceMethodsVMAddr
; 
5020             PtrTy    optionalClassMethodsVMAddr
; 
5021             PtrTy    instancePropertiesVMAddr
; 
5024             // Fields below this point are not always present on disk. 
5025             PtrTy    extendedMethodTypesVMAddr
; 
5026             PtrTy    demangledNameVMAddr
; 
5027             PtrTy    classPropertiesVMAddr
; 
5030         const protocol_list_t
* protoList 
= (const protocol_list_t
*)(protocolListVMAddr 
+ slide
); 
5031         for (PtrTy i 
= 0; i 
!= protoList
->count
; ++i
) { 
5032             uint64_t protocolVMAddr 
= vmAddrConverter
.convertToVMAddr(protoList
->array
[i
].refVMAddr
); 
5034             const protocol_t
* protocolPtr 
= (const protocol_t
*)(protocolVMAddr 
+ slide
); 
5035             ObjCProtocol objCProtocol
; 
5036             objCProtocol
.isaVMAddr                          
= vmAddrConverter
.convertToVMAddr(protocolPtr
->isaVMAddr
); 
5037             objCProtocol
.nameVMAddr                         
= vmAddrConverter
.convertToVMAddr(protocolPtr
->nameVMAddr
); 
5038             objCProtocol
.protocolsVMAddr                    
= vmAddrConverter
.convertToVMAddr(protocolPtr
->protocolsVMAddr
); 
5039             objCProtocol
.instanceMethodsVMAddr              
= vmAddrConverter
.convertToVMAddr(protocolPtr
->instanceMethodsVMAddr
); 
5040             objCProtocol
.classMethodsVMAddr                 
= vmAddrConverter
.convertToVMAddr(protocolPtr
->classMethodsVMAddr
); 
5041             objCProtocol
.optionalInstanceMethodsVMAddr      
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalInstanceMethodsVMAddr
); 
5042             objCProtocol
.optionalClassMethodsVMAddr         
= vmAddrConverter
.convertToVMAddr(protocolPtr
->optionalClassMethodsVMAddr
); 
5044             handler(protocolVMAddr
, objCProtocol
); 
5050 void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics
& diag
, const VMAddrConverter
& vmAddrConverter
, 
5051                                                  void (^handler
)(uint64_t selRefVMAddr
, uint64_t selRefTargetVMAddr
)) const { 
5052     const uint64_t ptrSize 
= pointerSize(); 
5053     intptr_t slide 
= getSlide(); 
5055     forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
5056         if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 ) 
5058         if ( strcmp(sectInfo
.sectName
, "__objc_selrefs") != 0 ) 
5060         uint64_t        selRefSectionVMAddr 
= sectInfo
.sectAddr
; 
5061         const uint8_t*  selRefs       
= (uint8_t*)(selRefSectionVMAddr 
+ slide
); 
5062         uint64_t        selRefsSize   
= sectInfo
.sectSize
; 
5064         if ( (selRefsSize 
% ptrSize
) != 0 ) { 
5065             diag
.error("Invalid sel ref section size"); 
5069         if ( ptrSize 
== 8 ) { 
5070             typedef uint64_t PtrTy
; 
5071             for (uint64_t i 
= 0; i 
!= selRefsSize
; i 
+= sizeof(PtrTy
)) { 
5072                 uint64_t selRefVMAddr 
= selRefSectionVMAddr 
+ i
; 
5073                 uint64_t selRefTargetVMAddr 
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(selRefs 
+ i
)); 
5074                 handler(selRefVMAddr
, selRefTargetVMAddr
); 
5075                 if (diag
.hasError()) { 
5081             typedef uint32_t PtrTy
; 
5082             for (uint64_t i 
= 0; i 
!= selRefsSize
; i 
+= sizeof(PtrTy
)) { 
5083                 uint64_t selRefVMAddr 
= selRefSectionVMAddr 
+ i
; 
5084                 uint64_t selRefTargetVMAddr 
= vmAddrConverter
.convertToVMAddr(*(PtrTy
*)(selRefs 
+ i
)); 
5085                 handler(selRefVMAddr
, selRefTargetVMAddr
); 
5086                 if (diag
.hasError()) { 
5095 void MachOAnalyzer::forEachObjCMethodName(void (^handler
)(const char* methodName
)) const { 
5096     intptr_t slide 
= getSlide(); 
5097     forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
5098         if ( strcmp(sectInfo
.segInfo
.segName
, "__TEXT") != 0 ) 
5100         if ( strcmp(sectInfo
.sectName
, "__objc_methname") != 0 ) 
5102         if ( sectInfo
.segInfo
.isProtected 
|| ( (sectInfo
.sectFlags 
& SECTION_TYPE
) != S_CSTRING_LITERALS 
) ) { 
5106         if ( malformedSectionRange 
) { 
5111         const char* content       
= (const char*)(sectInfo
.sectAddr 
+ slide
); 
5112         uint64_t    sectionSize   
= sectInfo
.sectSize
; 
5114         const char* s   
= (const char*)content
; 
5115         const char* end 
= s 
+ sectionSize
; 
5124 bool MachOAnalyzer::hasObjCMessageReferences() const { 
5126     __block 
bool foundSection 
= false; 
5127     forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
5128         if ( strncmp(sectInfo
.segInfo
.segName
, "__DATA", 6) != 0 ) 
5130         if ( strcmp(sectInfo
.sectName
, "__objc_msgrefs") != 0 ) 
5132         foundSection 
= true; 
5135     return foundSection
; 
5138 const MachOAnalyzer::ObjCImageInfo
* MachOAnalyzer::objcImageInfo() const { 
5139     int64_t slide 
= getSlide(); 
5141     __block 
bool foundInvalidObjCImageInfo 
= false; 
5142     __block 
const ObjCImageInfo
* imageInfo 
= nullptr; 
5143     forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& sectionInfo
, bool malformedSectionRange
, bool& stop
) { 
5144         if ( strncmp(sectionInfo
.segInfo
.segName
, "__DATA", 6) != 0 ) 
5146         if (strcmp(sectionInfo
.sectName
, "__objc_imageinfo") != 0) 
5148         if ( malformedSectionRange 
) { 
5152         if ( sectionInfo
.sectSize 
!= 8 ) { 
5156         imageInfo 
= (const ObjCImageInfo
*)(sectionInfo
.sectAddr 
+ slide
); 
5157         if ( (imageInfo
->flags 
& ObjCImageInfo::dyldPreoptimized
) != 0 ) { 
5158             foundInvalidObjCImageInfo 
= true; 
5164     if ( foundInvalidObjCImageInfo 
) 
5169 uint32_t MachOAnalyzer::loadCommandsFreeSpace() const 
5171     __block 
uint32_t firstSectionFileOffset 
= 0; 
5172     __block 
uint32_t firstSegmentFileOffset 
= 0; 
5173     forEachSection(^(const SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
5174         firstSectionFileOffset 
= sectInfo
.sectFileOffset
; 
5175         firstSegmentFileOffset 
= (uint32_t)sectInfo
.segInfo
.fileOffset
; 
5179     uint32_t headerSize 
= (this->magic 
== MH_MAGIC_64
) ? sizeof(mach_header_64
) : sizeof(mach_header
); 
5180     uint32_t existSpaceUsed 
= this->sizeofcmds 
+ headerSize
; 
5181     return firstSectionFileOffset 
- firstSegmentFileOffset 
- existSpaceUsed
; 
5184 void MachOAnalyzer::forEachWeakDef(Diagnostics
& diag
, 
5185                                    void (^handler
)(const char* symbolName
, uint64_t imageOffset
, bool isFromExportTrie
)) const { 
5186     uint64_t baseAddress 
= preferredLoadAddress(); 
5187     forEachGlobalSymbol(diag
, ^(const char *symbolName
, uint64_t n_value
, uint8_t n_type
, uint8_t n_sect
, uint16_t n_desc
, bool &stop
) { 
5188         if ( (n_desc 
& N_WEAK_DEF
) != 0 ) { 
5189             handler(symbolName
, n_value 
- baseAddress
, false); 
5192     forEachExportedSymbol(diag
, ^(const char *symbolName
, uint64_t imageOffset
, uint64_t flags
, uint64_t other
, const char *importName
, bool &stop
) { 
5193         if ( (flags 
& EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 
) == 0 ) 
5195         // Skip resolvers and re-exports 
5196         if ( (flags 
& EXPORT_SYMBOL_FLAGS_REEXPORT 
) != 0 ) 
5198         if ( (flags 
& EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 
) != 0 ) 
5200         handler(symbolName
, imageOffset
, true);