1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
   3  * Copyright (c) 2009-2012 Apple Inc. All rights reserved. 
   5  * @APPLE_LICENSE_HEADER_START@ 
   7  * This file contains Original Code and/or Modifications of Original Code 
   8  * as defined in and that are subject to the Apple Public Source License 
   9  * Version 2.0 (the 'License'). You may not use this file except in 
  10  * compliance with the License. Please obtain a copy of the License at 
  11  * http://www.opensource.apple.com/apsl/ and read it before using this 
  14  * The Original Code and all software distributed under the License are 
  15  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  16  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  17  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  18  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  19  * Please see the License for the specific language governing rights and 
  20  * limitations under the License. 
  22  * @APPLE_LICENSE_HEADER_END@ 
  34 #include <sys/syslimits.h> 
  35 #include <mach-o/arch.h> 
  36 #include <mach-o/loader.h> 
  37 #include <mach-o/dyld_priv.h> 
  38 #include <bootstrap.h> 
  39 #include <mach/mach.h> 
  40 #include <dispatch/dispatch.h> 
  41 #include <uuid/uuid.h> 
  43 #include <TargetConditionals.h> 
  50 #include "ClosureBuilder.h" 
  51 #include "DyldSharedCache.h" 
  52 #include "ClosureFileSystemPhysical.h" 
  53 #include "JSONWriter.h" 
  55 #include "dsc_extractor.h" 
  57 #include "objc-shared-cache.h" 
  60 #define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle" 
  62 #define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle" 
  65 using dyld3::closure::ClosureBuilder
; 
  66 using dyld3::closure::FileSystemPhysical
; 
  68 // mmap() an shared cache file read/only but laid out like it would be at runtime 
  69 static const DyldSharedCache
* mapCacheFile(const char* path
) 
  72     if ( ::stat(path
, &statbuf
) ) { 
  73         fprintf(stderr
, "Error: stat failed for dyld shared cache at %s\n", path
); 
  77     int cache_fd 
= ::open(path
, O_RDONLY
); 
  79         fprintf(stderr
, "Error: failed to open shared cache file at %s\n", path
); 
  83     uint8_t  firstPage
[4096]; 
  84     if ( ::pread(cache_fd
, firstPage
, 4096, 0) != 4096 ) { 
  85         fprintf(stderr
, "Error: failed to read shared cache file at %s\n", path
); 
  88     const dyld_cache_header
*       header   
= (dyld_cache_header
*)firstPage
; 
  89     const dyld_cache_mapping_info
* mappings 
= (dyld_cache_mapping_info
*)(firstPage 
+ header
->mappingOffset
); 
  90     const dyld_cache_mapping_info
* lastMapping 
= &mappings
[header
->mappingCount 
- 1]; 
  92     size_t vmSize 
= (size_t)(lastMapping
->address 
+ lastMapping
->size 
- mappings
[0].address
); 
  94     kern_return_t r 
= ::vm_allocate(mach_task_self(), &result
, vmSize
, VM_FLAGS_ANYWHERE
); 
  95     if ( r 
!= KERN_SUCCESS 
) { 
  96         fprintf(stderr
, "Error: failed to allocate space to load shared cache file at %s\n", path
); 
  99     for (int i
=0; i 
< header
->mappingCount
; ++i
) { 
 100         void* mapped_cache 
= ::mmap((void*)(result 
+ mappings
[i
].address 
- mappings
[0].address
), (size_t)mappings
[i
].size
, 
 101                                     PROT_READ
, MAP_FIXED 
| MAP_PRIVATE
, cache_fd
, mappings
[i
].fileOffset
); 
 102         if (mapped_cache 
== MAP_FAILED
) { 
 103             fprintf(stderr
, "Error: mmap() for shared cache at %s failed, errno=%d\n", path
, errno
); 
 109     return (DyldSharedCache
*)result
; 
 118     modeVerboseSlideInfo
, 
 134     modeListDylibsWithSection
 
 139     const char*     dependentsOfPath
; 
 140     const char*     extractionDir
; 
 141     const char*     segmentName
; 
 142     const char*     sectionName
; 
 145     bool            printDylibVersions
; 
 151     fprintf(stderr
, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map | -slide_info | -verbose_slide_info | -info | -extract <dylib-dir>  [ shared-cache-file ] \n"); 
 154 static void checkMode(Mode mode
) { 
 155     if ( mode 
!= modeNone 
) { 
 156         fprintf(stderr
, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n"); 
 166     const char* installName
; 
 170 static void buildSegmentInfo(const DyldSharedCache
* dyldCache
, std::vector
<SegmentInfo
>& segInfos
) 
 172     dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) { 
 173         dyld3::MachOAnalyzer
* ma 
= (dyld3::MachOAnalyzer
*)mh
; 
 174         ma
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo
& info
, bool& stop
) { 
 175             segInfos
.push_back({info
.vmAddr
, info
.vmSize
, installName
, info
.segName
}); 
 179     std::sort(segInfos
.begin(), segInfos
.end(), [](const SegmentInfo
& l
, const SegmentInfo
& r
) -> bool { 
 180         return l
.vmAddr 
< r
.vmAddr
; 
 184 static void printSlideInfoForDataRegion(const DyldSharedCache
* dyldCache
, uint64_t dataStartAddress
, uint64_t dataSize
, 
 185                                         const uint8_t* dataPagesStart
, 
 186                                         const dyld_cache_slide_info
* slideInfoHeader
, bool verboseSlideInfo
) { 
 188     printf("slide info version=%d\n", slideInfoHeader
->version
); 
 189     if ( slideInfoHeader
->version 
== 1 ) { 
 190         printf("toc_count=%d, data page count=%lld\n", slideInfoHeader
->toc_count
, dataSize
/4096); 
 191         const dyld_cache_slide_info_entry
* entries 
= (dyld_cache_slide_info_entry
*)((char*)slideInfoHeader 
+ slideInfoHeader
->entries_offset
); 
 192         const uint16_t* tocs 
= (uint16_t*)((char*)slideInfoHeader 
+ slideInfoHeader
->toc_offset
); 
 193         for(int i
=0; i 
< slideInfoHeader
->toc_count
; ++i
) { 
 194             printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress 
+ i
*4096, i
, tocs
[i
]); 
 195             const dyld_cache_slide_info_entry
* entry 
= &entries
[tocs
[i
]]; 
 196             for(int j
=0; j 
< slideInfoHeader
->entries_size
; ++j
) 
 197                 printf("%02X", entry
->bits
[j
]); 
 201     else if ( slideInfoHeader
->version 
== 2 ) { 
 202         const dyld_cache_slide_info2
* slideInfo 
= (dyld_cache_slide_info2
*)(slideInfoHeader
); 
 203         printf("page_size=%d\n", slideInfo
->page_size
); 
 204         printf("delta_mask=0x%016llX\n", slideInfo
->delta_mask
); 
 205         printf("value_add=0x%016llX\n", slideInfo
->value_add
); 
 206         printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo
->page_starts_count
, slideInfo
->page_extras_count
); 
 207         const uint16_t* starts 
= (uint16_t* )((char*)slideInfo 
+ slideInfo
->page_starts_offset
); 
 208         const uint16_t* extras 
= (uint16_t* )((char*)slideInfo 
+ slideInfo
->page_extras_offset
); 
 209         for (int i
=0; i 
< slideInfo
->page_starts_count
; ++i
) { 
 210             const uint16_t start 
= starts
[i
]; 
 211             auto rebaseChain 
= [&](uint8_t* pageContent
, uint16_t startOffset
) 
 213                 uintptr_t slideAmount 
= 0; 
 214                 const uintptr_t   deltaMask    
= (uintptr_t)(slideInfo
->delta_mask
); 
 215                 const uintptr_t   valueMask    
= ~deltaMask
; 
 216                 const uintptr_t   valueAdd     
= (uintptr_t)(slideInfo
->value_add
); 
 217                 const unsigned    deltaShift   
= __builtin_ctzll(deltaMask
) - 2; 
 219                 uint32_t pageOffset 
= startOffset
; 
 221                 while ( delta 
!= 0 ) { 
 222                     uint8_t* loc 
= pageContent 
+ pageOffset
; 
 223                     uintptr_t rawValue 
= *((uintptr_t*)loc
); 
 224                     delta 
= (uint32_t)((rawValue 
& deltaMask
) >> deltaShift
); 
 225                     uintptr_t value 
= (rawValue 
& valueMask
); 
 228                         value 
+= slideAmount
; 
 230                     printf("    [% 5d + 0x%04llX]: 0x%016llX = 0x%016llX\n", i
, (uint64_t)(pageOffset
), (uint64_t)rawValue
, (uint64_t)value
); 
 234             if ( start 
== DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 
) { 
 235                 printf("page[% 5d]: no rebasing\n", i
); 
 237             else if ( start 
& DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 
) { 
 238                 printf("page[% 5d]: ", i
); 
 239                 int j
=(start 
& 0x3FFF); 
 242                     uint16_t aStart 
= extras
[j
]; 
 243                     printf("start=0x%04X ", aStart 
& 0x3FFF); 
 244                     if ( verboseSlideInfo 
) { 
 245                         uint8_t* page 
= (uint8_t*)(long)(dataPagesStart 
+ (slideInfo
->page_size
*i
)); 
 246                         uint16_t pageStartOffset 
= (aStart 
& 0x3FFF)*4; 
 247                         rebaseChain(page
, pageStartOffset
); 
 249                     done 
= (extras
[j
] & DYLD_CACHE_SLIDE_PAGE_ATTR_END
); 
 255                 printf("page[% 5d]: start=0x%04X\n", i
, starts
[i
]); 
 256                 if ( verboseSlideInfo 
) { 
 257                     uint8_t* page 
= (uint8_t*)(long)(dataPagesStart 
+ (slideInfo
->page_size
*i
)); 
 258                     uint16_t pageStartOffset 
= start
*4; 
 259                     rebaseChain(page
, pageStartOffset
); 
 264     else if ( slideInfoHeader
->version 
== 3 ) { 
 265         const dyld_cache_slide_info3
* slideInfo 
= (dyld_cache_slide_info3
*)(slideInfoHeader
); 
 266         printf("page_size=%d\n", slideInfo
->page_size
); 
 267         printf("page_starts_count=%d\n", slideInfo
->page_starts_count
); 
 268         printf("auth_value_add=0x%016llX\n", slideInfo
->auth_value_add
); 
 269         const uintptr_t authValueAdd 
= (uintptr_t)(slideInfo
->auth_value_add
); 
 270         for (int i
=0; i 
< slideInfo
->page_starts_count
; ++i
) { 
 271             uint16_t delta 
= slideInfo
->page_starts
[i
]; 
 272             if ( delta 
== DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE 
) { 
 273                 printf("page[% 5d]: no rebasing\n", i
); 
 277             printf("page[% 5d]: start=0x%04X\n", i
, delta
); 
 278             if ( !verboseSlideInfo 
) 
 281             delta 
= delta
/sizeof(uint64_t); // initial offset is byte based 
 282             const uint8_t* pageStart 
= dataPagesStart 
+ (i 
* slideInfo
->page_size
); 
 283             const dyld_cache_slide_pointer3
* loc 
= (dyld_cache_slide_pointer3
*)pageStart
; 
 286                 delta 
= loc
->plain
.offsetToNextPointer
; 
 287                 dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr
; 
 288                 ptr
.raw64 
= *((uint64_t*)loc
); 
 289                 if ( loc
->auth
.authenticated 
) { 
 290                     uint64_t target 
= authValueAdd 
+ loc
->auth
.offsetFromSharedCacheBase
; 
 291                     uint64_t targetValue 
= ptr
.arm64e
.signPointer((void*)loc
, target
); 
 292                     printf("    [% 5d + 0x%04llX]: 0x%016llX (JOP: diversity %d, address %s, %s)\n", 
 293                            i
, (uint64_t)((const uint8_t*)loc 
- pageStart
), targetValue
, 
 294                            ptr
.arm64e
.authBind
.diversity
, ptr
.arm64e
.authBind
.addrDiv 
? "true" : "false", 
 295                            ptr
.arm64e
.keyName()); 
 298                     uint64_t targetValue 
= ptr
.arm64e
.unpackTarget(); 
 299                     printf("    [% 5d + 0x%04llX]: 0x%016llX\n", i
, (uint64_t)((const uint8_t*)loc 
- pageStart
), targetValue
); 
 301             } while (delta 
!= 0); 
 304     else if ( slideInfoHeader
->version 
== 4 ) { 
 305         const dyld_cache_slide_info4
* slideInfo 
= (dyld_cache_slide_info4
*)(slideInfoHeader
); 
 306         printf("page_size=%d\n", slideInfo
->page_size
); 
 307         printf("delta_mask=0x%016llX\n", slideInfo
->delta_mask
); 
 308         printf("value_add=0x%016llX\n", slideInfo
->value_add
); 
 309         printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo
->page_starts_count
, slideInfo
->page_extras_count
); 
 310         const uint16_t* starts 
= (uint16_t* )((char*)slideInfo 
+ slideInfo
->page_starts_offset
); 
 311         const uint16_t* extras 
= (uint16_t* )((char*)slideInfo 
+ slideInfo
->page_extras_offset
); 
 312         for (int i
=0; i 
< slideInfo
->page_starts_count
; ++i
) { 
 313             const uint16_t start 
= starts
[i
]; 
 314             auto rebaseChainV4 
= [&](uint8_t* pageContent
, uint16_t startOffset
) 
 316                 uintptr_t slideAmount 
= 0; 
 317                 const uintptr_t   deltaMask    
= (uintptr_t)(slideInfo
->delta_mask
); 
 318                 const uintptr_t   valueMask    
= ~deltaMask
; 
 319                 const uintptr_t   valueAdd     
= (uintptr_t)(slideInfo
->value_add
); 
 320                 const unsigned    deltaShift   
= __builtin_ctzll(deltaMask
) - 2; 
 322                 uint32_t pageOffset 
= startOffset
; 
 324                 while ( delta 
!= 0 ) { 
 325                     uint8_t* loc 
= pageContent 
+ pageOffset
; 
 326                     uint32_t rawValue 
= *((uint32_t*)loc
); 
 327                     delta 
= (uint32_t)((rawValue 
& deltaMask
) >> deltaShift
); 
 328                     uintptr_t value 
= (rawValue 
& valueMask
); 
 329                     if ( (value 
& 0xFFFF8000) == 0 ) { 
 330                         // small positive non-pointer, use as-is 
 332                     else if ( (value 
& 0x3FFF8000) == 0x3FFF8000 ) { 
 333                         // small negative non-pointer 
 338                         value 
+= slideAmount
; 
 340                     printf("    [% 5d + 0x%04X]: 0x%08X\n", i
, pageOffset
, rawValue
); 
 344             if ( start 
== DYLD_CACHE_SLIDE4_PAGE_NO_REBASE 
) { 
 345                 printf("page[% 5d]: no rebasing\n", i
); 
 347             else if ( start 
& DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA 
) { 
 348                 printf("page[% 5d]: ", i
); 
 349                 int j
=(start 
& DYLD_CACHE_SLIDE4_PAGE_INDEX
); 
 352                     uint16_t aStart 
= extras
[j
]; 
 353                     printf("start=0x%04X ", aStart 
& DYLD_CACHE_SLIDE4_PAGE_INDEX
); 
 354                     if ( verboseSlideInfo 
) { 
 355                         uint8_t* page 
= (uint8_t*)(long)(dataPagesStart 
+ (slideInfo
->page_size
*i
)); 
 356                         uint16_t pageStartOffset 
= (aStart 
& DYLD_CACHE_SLIDE4_PAGE_INDEX
)*4; 
 357                         rebaseChainV4(page
, pageStartOffset
); 
 359                     done 
= (extras
[j
] & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END
); 
 365                 printf("page[% 5d]: start=0x%04X\n", i
, starts
[i
]); 
 366                 if ( verboseSlideInfo 
) { 
 367                     uint8_t* page 
= (uint8_t*)(long)(dataPagesStart 
+ (slideInfo
->page_size
*i
)); 
 368                     uint16_t pageStartOffset 
= start
*4; 
 369                     rebaseChainV4(page
, pageStartOffset
); 
 377 static void findImageAndSegment(const DyldSharedCache
* dyldCache
, const std::vector
<SegmentInfo
>& segInfos
, uint32_t cacheOffset
, SegmentInfo
* found
) 
 379     const uint64_t locVmAddr 
= dyldCache
->unslidLoadAddress() + cacheOffset
; 
 380     const SegmentInfo target 
= { locVmAddr
, 0, NULL
, NULL 
}; 
 381     const auto lowIt 
= std::lower_bound(segInfos
.begin(), segInfos
.end(), target
, 
 382                                                                         [](const SegmentInfo
& l
, const SegmentInfo
& r
) -> bool { 
 383                                                                             return l
.vmAddr
+l
.vmSize 
< r
.vmAddr
+r
.vmSize
; 
 389 int main (int argc
, const char* argv
[]) { 
 391     const char* sharedCachePath 
= nullptr; 
 394     options
.mode 
= modeNone
; 
 395     options
.printUUIDs 
= false; 
 396     options
.printVMAddrs 
= false; 
 397     options
.printDylibVersions 
= false; 
 398     options
.printInodes 
= false; 
 399     options
.dependentsOfPath 
= NULL
; 
 400     options
.extractionDir 
= NULL
; 
 402     bool printStrings 
= false; 
 403     bool printExports 
= false; 
 405     for (uint32_t i 
= 1; i 
< argc
; i
++) { 
 406         const char* opt 
= argv
[i
]; 
 408             if (strcmp(opt
, "-list") == 0) { 
 409                 checkMode(options
.mode
); 
 410                 options
.mode 
= modeList
; 
 412             else if (strcmp(opt
, "-dependents") == 0) { 
 413                 checkMode(options
.mode
); 
 414                 options
.mode 
= modeDependencies
; 
 415                 options
.dependentsOfPath 
= argv
[++i
]; 
 417                     fprintf(stderr
, "Error: option -depdendents requires an argument\n"); 
 422             else if (strcmp(opt
, "-linkedit") == 0) { 
 423                 checkMode(options
.mode
); 
 424                 options
.mode 
= modeLinkEdit
; 
 426             else if (strcmp(opt
, "-info") == 0) { 
 427                 checkMode(options
.mode
); 
 428                 options
.mode 
= modeInfo
; 
 430             else if (strcmp(opt
, "-slide_info") == 0) { 
 431                 checkMode(options
.mode
); 
 432                 options
.mode 
= modeSlideInfo
; 
 434             else if (strcmp(opt
, "-verbose_slide_info") == 0) { 
 435                 checkMode(options
.mode
); 
 436                 options
.mode 
= modeVerboseSlideInfo
; 
 438             else if (strcmp(opt
, "-text_info") == 0) { 
 439                 checkMode(options
.mode
); 
 440                 options
.mode 
= modeTextInfo
; 
 442             else if (strcmp(opt
, "-local_symbols") == 0) { 
 443                 checkMode(options
.mode
); 
 444                 options
.mode 
= modeLocalSymbols
; 
 446             else if (strcmp(opt
, "-strings") == 0) { 
 447                 if (options
.mode 
!= modeStrings
) 
 448                     checkMode(options
.mode
); 
 449                 options
.mode 
= modeStrings
; 
 452             else if (strcmp(opt
, "-sections") == 0) { 
 453                 checkMode(options
.mode
); 
 454                 options
.mode 
= modeSectionSizes
; 
 456             else if (strcmp(opt
, "-exports") == 0) { 
 457                 if (options
.mode 
!= modeStrings
) 
 458                     checkMode(options
.mode
); 
 459                 options
.mode 
= modeStrings
; 
 462             else if (strcmp(opt
, "-map") == 0) { 
 463                 checkMode(options
.mode
); 
 464                 options
.mode 
= modeMap
; 
 466             else if (strcmp(opt
, "-json-map") == 0) { 
 467                 checkMode(options
.mode
); 
 468                 options
.mode 
= modeJSONMap
; 
 470             else if (strcmp(opt
, "-json-dependents") == 0) { 
 471                 checkMode(options
.mode
); 
 472                 options
.mode 
= modeJSONDependents
; 
 474             else if (strcmp(opt
, "-size") == 0) { 
 475                 checkMode(options
.mode
); 
 476                 options
.mode 
= modeSize
; 
 478             else if (strcmp(opt
, "-objc-protocols") == 0) { 
 479                 checkMode(options
.mode
); 
 480                 options
.mode 
= modeObjCProtocols
; 
 482             else if (strcmp(opt
, "-objc-imp-caches") == 0) { 
 483                 checkMode(options
.mode
); 
 484                 options
.mode 
= modeObjCImpCaches
; 
 486             else if (strcmp(opt
, "-objc-classes") == 0) { 
 487                 checkMode(options
.mode
); 
 488                 options
.mode 
= modeObjCClasses
; 
 490             else if (strcmp(opt
, "-objc-selectors") == 0) { 
 491                 checkMode(options
.mode
); 
 492                 options
.mode 
= modeObjCSelectors
; 
 494             else if (strcmp(opt
, "-extract") == 0) { 
 495                 checkMode(options
.mode
); 
 496                 options
.mode 
= modeExtract
; 
 497                 options
.extractionDir 
= argv
[++i
]; 
 499                     fprintf(stderr
, "Error: option -extract requires a directory argument\n"); 
 504             else if (strcmp(opt
, "-uuid") == 0) { 
 505                 options
.printUUIDs 
= true; 
 507             else if (strcmp(opt
, "-inode") == 0) { 
 508                 options
.printInodes 
= true; 
 510             else if (strcmp(opt
, "-versions") == 0) { 
 511                 options
.printDylibVersions 
= true; 
 513             else if (strcmp(opt
, "-vmaddr") == 0) { 
 514                 options
.printVMAddrs 
= true; 
 516             else if (strcmp(opt
, "-patch_table") == 0) { 
 517                 options
.mode 
= modePatchTable
; 
 519             else if (strcmp(opt
, "-list_dylibs_with_section") == 0) { 
 520                 options
.mode 
= modeListDylibsWithSection
; 
 521                 options
.segmentName 
= argv
[++i
]; 
 522                 options
.sectionName 
= argv
[++i
]; 
 524                     fprintf(stderr
, "Error: option -list_dylibs_with_section requires a segment and section name\n"); 
 530                 fprintf(stderr
, "Error: unrecognized option %s\n", opt
); 
 536             sharedCachePath 
= opt
; 
 540     if ( options
.mode 
== modeNone 
) { 
 541         fprintf(stderr
, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n"); 
 546     if ( options
.mode 
!= modeSlideInfo 
&& options
.mode 
!= modeVerboseSlideInfo 
) { 
 547         if ( options
.printUUIDs 
&& (options
.mode 
!= modeList
) ) 
 548             fprintf(stderr
, "Warning: -uuid option ignored outside of -list mode\n"); 
 550         if ( options
.printVMAddrs 
&& (options
.mode 
!= modeList
) ) 
 551             fprintf(stderr
, "Warning: -vmaddr option ignored outside of -list mode\n"); 
 553         if ( options
.printDylibVersions 
&& (options
.mode 
!= modeDependencies
) ) 
 554             fprintf(stderr
, "Warning: -versions option ignored outside of -dependents mode\n"); 
 556         if ( (options
.mode 
== modeDependencies
) && (options
.dependentsOfPath 
== NULL
) ) { 
 557             fprintf(stderr
, "Error: -dependents given, but no dylib path specified\n"); 
 563     const DyldSharedCache
* dyldCache 
= nullptr; 
 564     if ( sharedCachePath 
!= nullptr ) { 
 565         dyldCache 
= mapCacheFile(sharedCachePath
); 
 566         // mapCacheFile prints an error if something goes wrong, so just return in that case. 
 567         if ( dyldCache 
== nullptr ) 
 572         dyldCache 
= (DyldSharedCache
*)_dyld_get_shared_cache_range(&cacheLength
); 
 573         if (dyldCache 
== nullptr) { 
 574             fprintf(stderr
, "Could not get in-memory shared cache\n"); 
 577         if ( options
.mode 
== modeObjCClasses 
) { 
 578             fprintf(stderr
, "Cannot use -objc-classes with a live cache.  Please run with a path to an on-disk cache file\n"); 
 583     if ( options
.mode 
== modeSlideInfo 
|| options
.mode 
== modeVerboseSlideInfo 
) { 
 584         if ( !dyldCache
->hasSlideInfo() ) { 
 585             fprintf(stderr
, "Error: dyld shared cache does not contain slide info\n"); 
 589         const bool verboseSlideInfo 
= (options
.mode 
== modeVerboseSlideInfo
); 
 590         dyldCache
->forEachSlideInfo(^(uint64_t mappingStartAddress
, uint64_t mappingSize
, const uint8_t *mappingPagesStart
, 
 591                                       uint64_t slideInfoOffset
, uint64_t slideInfoSize
, const dyld_cache_slide_info 
*slideInfoHeader
) { 
 592             printSlideInfoForDataRegion(dyldCache
, mappingStartAddress
, mappingSize
, mappingPagesStart
, 
 593                                         slideInfoHeader
, verboseSlideInfo
); 
 597     else if ( options
.mode 
== modeInfo 
) { 
 598         const dyld_cache_header
* header 
= &dyldCache
->header
; 
 599         uuid_string_t uuidString
; 
 600         uuid_unparse_upper(header
->uuid
, uuidString
); 
 601         printf("uuid: %s\n", uuidString
); 
 603         dyld3::Platform platform 
= dyldCache
->platform(); 
 604         printf("platform: %s\n", dyld3::MachOFile::platformName(platform
)); 
 605         printf("built by: %s\n", header
->locallyBuiltCache 
? "local machine" : "B&I"); 
 606         printf("cache type: %s\n", header
->cacheType 
? "production" : "development"); 
 607         printf("image count: %u\n", header
->imagesCount
); 
 608         if ( (header
->mappingOffset 
>= 0x78) && (header
->branchPoolsOffset 
!= 0) ) { 
 609             printf("branch pool count:  %u\n", header
->branchPoolsCount
); 
 611         if ( dyldCache
->hasSlideInfo() ) { 
 612             uint32_t pageSize            
= 0x4000; // fix me for intel 
 613             uint32_t possibleSlideValues 
= (uint32_t)(header
->maxSlide
/pageSize
); 
 614             uint32_t entropyBits 
= 0; 
 615             if ( possibleSlideValues 
> 1 ) 
 616                 entropyBits 
= __builtin_clz(possibleSlideValues 
- 1); 
 617             printf("ASLR entropy: %u-bits\n", entropyBits
); 
 619         printf("mappings:\n"); 
 620         dyldCache
->forEachRegion(^(const void *content
, uint64_t vmAddr
, uint64_t size
, 
 621                                    uint32_t initProt
, uint32_t maxProt
, uint64_t flags
) { 
 622             std::string mappingName 
= ""; 
 623             if ( maxProt 
& VM_PROT_EXECUTE 
) { 
 624                 mappingName 
= "__TEXT"; 
 625             } else if ( maxProt 
& VM_PROT_WRITE 
) { 
 626                 // Start off with __DATA or __AUTH 
 627                 if ( flags 
& DYLD_CACHE_MAPPING_AUTH_DATA 
) 
 628                     mappingName 
= "__AUTH"; 
 630                     mappingName 
= "__DATA"; 
 631                 // Then add one of "", _DIRTY, or _CONST 
 632                 if ( flags 
& DYLD_CACHE_MAPPING_DIRTY_DATA 
) 
 633                     mappingName 
+= "_DIRTY"; 
 634                 else if ( flags 
& DYLD_CACHE_MAPPING_CONST_DATA 
) 
 635                     mappingName 
+= "_CONST"; 
 637             else if ( maxProt 
& VM_PROT_READ 
) { 
 638                 mappingName 
= "__LINKEDIT"; 
 640                 mappingName 
= "*unknown*"; 
 642             uint64_t fileOffset 
= (uint8_t*)content 
- (uint8_t*)dyldCache
; 
 643             printf("%16s %4lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n", 
 644                    mappingName
.c_str(), size 
/ (1024*1024), fileOffset
, fileOffset 
+ size
, vmAddr
, vmAddr 
+ size
); 
 646         if ( header
->codeSignatureOffset 
!= 0 ) { 
 647             uint64_t size 
= header
->codeSignatureSize
; 
 648             uint64_t csAddr 
= dyldCache
->getCodeSignAddress(); 
 650                 printf("%16s %4lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n", 
 651                        "code sign", size
/(1024*1024), header
->codeSignatureOffset
, header
->codeSignatureOffset 
+ size
, csAddr
, csAddr 
+ size
); 
 653         dyldCache
->forEachSlideInfo(^(uint64_t mappingStartAddress
, uint64_t mappingSize
, const uint8_t *mappingPagesStart
, 
 654                                       uint64_t slideInfoOffset
, uint64_t slideInfoSize
, const dyld_cache_slide_info 
*slideInfoHeader
) { 
 656             printf("slide info:      %4lluKB,  file offset: 0x%08llX -> 0x%08llX\n", 
 657                    slideInfoSize
/1024, slideInfoOffset
, slideInfoOffset 
+ slideInfoSize
); 
 659         if ( header
->localSymbolsOffset 
!= 0 ) 
 660             printf("local symbols:    %3lluMB,  file offset: 0x%08llX -> 0x%08llX\n", 
 661                    header
->localSymbolsSize
/(1024*1024), header
->localSymbolsOffset
, header
->localSymbolsOffset 
+ header
->localSymbolsSize
); 
 663     else if ( options
.mode 
== modeTextInfo 
) { 
 664         const dyld_cache_header
* header 
= &dyldCache
->header
; 
 665         printf("dylib text infos (count=%llu):\n", header
->imagesTextCount
); 
 666         dyldCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const unsigned char *dylibUUID
, const char *installName
, bool &stop
) { 
 667             uuid_string_t uuidString
; 
 668             uuid_unparse_upper(dylibUUID
, uuidString
); 
 669             printf("   0x%09llX -> 0x%09llX  <%s>  %s\n", loadAddressUnslid
, loadAddressUnslid 
+ textSegmentSize
, uuidString
, installName
); 
 672     else if ( options
.mode 
== modeLocalSymbols 
) { 
 673         if ( !dyldCache
->hasLocalSymbolsInfo() ) { 
 674             fprintf(stderr
, "Error: dyld shared cache does not contain local symbols info\n"); 
 677         const bool is64 
= (strstr(dyldCache
->archName(), "64") != NULL
); 
 678         const uint32_t nlistFileOffset 
= (uint32_t)((uint8_t*)dyldCache
->getLocalNlistEntries() - (uint8_t*)dyldCache
); 
 679         const uint32_t nlistCount 
= dyldCache
->getLocalNlistCount(); 
 680         const uint32_t nlistByteSize 
= is64 
? nlistCount
*16 : nlistCount
*12; 
 681         const char* localStrings 
= dyldCache
->getLocalStrings(); 
 682         const uint32_t stringsFileOffset 
= (uint32_t)((uint8_t*)localStrings 
- (uint8_t*)dyldCache
); 
 683         const uint32_t stringsSize 
= dyldCache
->getLocalStringsSize(); 
 685         printf("local symbols nlist array:  %3uMB,  file offset: 0x%08X -> 0x%08X\n", nlistByteSize
/(1024*1024), nlistFileOffset
, nlistFileOffset
+nlistByteSize
); 
 686         printf("local symbols string pool:  %3uMB,  file offset: 0x%08X -> 0x%08X\n", stringsSize
/(1024*1024), stringsFileOffset
, stringsFileOffset
+stringsSize
); 
 688         __block 
uint32_t entriesCount 
= 0; 
 689         dyldCache
->forEachLocalSymbolEntry(^(uint32_t dylibOffset
, uint32_t nlistStartIndex
, uint32_t nlistCount
, bool &stop
) { 
 690             const char* imageName 
= dyldCache
->getIndexedImagePath(entriesCount
); 
 691             printf("   nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", nlistStartIndex
, nlistCount
, imageName
); 
 694                 const nlist_64
* symTab 
= (nlist_64
*)((char*)dyldCache 
+ nlistFileOffset
); 
 695                 for (int e 
= 0; e 
< nlistCount
; ++e
) { 
 696                     const nlist_64
* entry 
= &symTab
[nlistStartIndex 
+ e
]; 
 697                     printf("     nlist[%d].str=%d, %s\n", e
, entry
->n_un
.n_strx
, &localStrings
[entry
->n_un
.n_strx
]); 
 698                     printf("     nlist[%d].value=0x%0llX\n", e
, entry
->n_value
); 
 704         printf("local symbols by dylib (count=%d):\n", entriesCount
); 
 706     else if ( options
.mode 
== modeJSONMap 
) { 
 707         std::string buffer 
= dyldCache
->generateJSONMap("unknown"); 
 708         printf("%s\n", buffer
.c_str()); 
 710     else if ( options
.mode 
== modeJSONDependents 
) { 
 711         std::cout 
<<  dyldCache
->generateJSONDependents(); 
 713     else if ( options
.mode 
== modeStrings 
) { 
 715             dyldCache
->forEachImage(^(const mach_header 
*mh
, const char *installName
) { 
 716                 const dyld3::MachOAnalyzer
* ma 
= (dyld3::MachOAnalyzer
*)mh
; 
 717                 int64_t slide 
= ma
->getSlide(); 
 718                 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) { 
 719                     if ( ( (info
.sectFlags 
& SECTION_TYPE
) == S_CSTRING_LITERALS 
) ) { 
 720                         if ( malformedSectionRange 
) { 
 724                         const uint8_t* content 
= (uint8_t*)(info
.sectAddr 
+ slide
); 
 725                         const char* s   
= (char*)content
; 
 726                         const char* end 
= s 
+ info
.sectSize
; 
 728                             printf("%s: %s\n", ma
->installName(), s
); 
 739             dyldCache
->forEachImage(^(const mach_header 
*mh
, const char *installName
) { 
 740                 const dyld3::MachOAnalyzer
* ma 
= (dyld3::MachOAnalyzer
*)mh
; 
 741                 uint32_t exportTrieRuntimeOffset
; 
 742                 uint32_t exportTrieSize
; 
 743                 if ( ma
->hasExportTrie(exportTrieRuntimeOffset
, exportTrieSize
) ) { 
 744                     const uint8_t* start 
= (uint8_t*)mh 
+ exportTrieRuntimeOffset
; 
 745                     const uint8_t* end 
= start 
+ exportTrieSize
; 
 746                     std::vector
<ExportInfoTrie::Entry
> exports
; 
 747                     if ( !ExportInfoTrie::parseTrie(start
, end
, exports
) ) { 
 751                     for (const ExportInfoTrie::Entry
& entry
: exports
) { 
 752                         printf("%s: %s\n", ma
->installName(), entry
.name
.c_str()); 
 758     else if ( options
.mode 
== modeSectionSizes 
) { 
 759         __block 
std::map
<std::string
, uint64_t> sectionSizes
; 
 760         dyldCache
->forEachImage(^(const mach_header 
*mh
, const char *installName
) { 
 761             const dyld3::MachOAnalyzer
* ma 
= (const dyld3::MachOAnalyzer
*)mh
; 
 762             ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo 
§Info
, bool malformedSectionRange
, bool &stop
) { 
 763                 std::string section 
= std::string(sectInfo
.segInfo
.segName
) + " " + sectInfo
.sectName
; 
 764                 sectionSizes
[section
] += sectInfo
.sectSize
; 
 767         for (const auto& keyAndValue 
: sectionSizes
) { 
 768             printf("%lld %s\n", keyAndValue
.second
, keyAndValue
.first
.c_str()); 
 771     else if ( options
.mode 
== modeObjCProtocols 
) { 
 772         if ( dyldCache
->objcOpt() == nullptr ) { 
 773             fprintf(stderr
, "Error: could not get optimized objc\n"); 
 776         objc_opt::objc_protocolopt2_t
* protocols 
= dyldCache
->objcOpt()->protocolopt2(); 
 777         if ( protocols 
== nullptr ) { 
 778             fprintf(stderr
, "Error: could not get optimized objc protocols\n"); 
 782         for (uint64_t index 
= 0; index 
!= protocols
->capacity
; ++index
) { 
 783             const objc_opt::objc_classheader_t
& clshi 
= protocols
->classOffsets()[index
]; 
 784             if ( clshi
.clsOffset 
== 0 ) { 
 785                 fprintf(stderr
, "[% 5lld]\n", index
); 
 788             const char* name 
= (const char*)(((const uint8_t*)protocols
) + protocols
->offsets()[index
]); 
 789             if ( !clshi
.isDuplicate() ) { 
 790                 fprintf(stderr
, "[% 5lld] -> (% 8d, % 8d) = %s\n", index
, clshi
.clsOffset
, clshi
.hiOffset
, name
); 
 794             // class appears in more than one header 
 795             uint32_t count 
= clshi
.duplicateCount(); 
 796             fprintf(stderr
, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n", 
 797                     index
, clshi
.duplicateIndex(), clshi
.duplicateIndex() + clshi
.duplicateCount() - 1, name
); 
 799             const objc_opt::objc_classheader_t 
*list 
= &protocols
->duplicateOffsets()[clshi
.duplicateIndex()]; 
 800             for (uint32_t i 
= 0; i 
< count
; i
++) { 
 801                 fprintf(stderr
, "  - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi
.duplicateIndex() + i
), list
[i
].clsOffset
, list
[i
].hiOffset
); 
 805     else if ( options
.mode 
== modeObjCClasses 
) { 
 806         using dyld3::json::Node
; 
 807         using dyld3::json::NodeValueType
; 
 808         using ObjCClassInfo 
= dyld3::MachOAnalyzer::ObjCClassInfo
; 
 809         const bool rebased 
= false; 
 811         std::string 
instancePrefix("-"); 
 812         std::string 
classPrefix("+"); 
 814         auto getString 
= ^const char *(const dyld3::MachOAnalyzer
* ma
, uint64_t nameVMAddr
){ 
 815             dyld3::MachOAnalyzer::PrintableStringResult result
; 
 816             const char* name 
= ma
->getPrintableString(nameVMAddr
, result
); 
 817             if (result 
== dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
) 
 822         // Build a map of class vm addrs to their names so that categories know the 
 823         // name of the class they are attaching to 
 824         __block 
std::unordered_map
<uint64_t, const char*> classVMAddrToName
; 
 825         __block 
std::unordered_map
<uint64_t, const char*> metaclassVMAddrToName
; 
 826         dyldCache
->forEachImage(^(const mach_header 
*mh
, const char *installName
) { 
 827             const dyld3::MachOAnalyzer
* ma 
= (const dyld3::MachOAnalyzer
*)mh
; 
 828             const uint32_t pointerSize 
= ma
->pointerSize(); 
 830             auto visitClass 
= ^(Diagnostics
& diag
, uint64_t classVMAddr
, 
 831                                 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, 
 832                                 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) { 
 833                 if (auto className 
= getString(ma
, objcClass
.nameVMAddr(pointerSize
))) { 
 835                         metaclassVMAddrToName
[classVMAddr
] = className
; 
 837                         classVMAddrToName
[classVMAddr
] = className
; 
 843             dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter 
= dyldCache
->makeVMAddrConverter(rebased
); 
 844             ma
->forEachObjCClass(diag
, vmAddrConverter
, visitClass
); 
 847         // These are used only for the on-disk binaries we analyze 
 848         __block 
std::vector
<const char*>        onDiskChainedFixupBindTargets
; 
 849         __block 
std::unordered_map
<uint64_t, const char*> onDiskClassVMAddrToName
; 
 850         __block 
std::unordered_map
<uint64_t, const char*> onDiskMetaclassVMAddrToName
; 
 852         auto getProperties 
= ^(const dyld3::MachOAnalyzer
* ma
, uint64_t propertiesVMAddr
, 
 853                                const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) { 
 854             __block Node propertiesNode
; 
 855             auto visitProperty 
= ^(uint64_t propertyVMAddr
, const dyld3::MachOAnalyzer::ObjCProperty
& property
) { 
 856                 // Get the name && attributes 
 857                 auto propertyName 
= getString(ma
, property
.nameVMAddr
); 
 858                 auto propertyAttributes 
= getString(ma
, property
.attributesVMAddr
); 
 860                 if (!propertyName 
|| !propertyAttributes
) 
 864                 propertyNode
.map
["name"] = Node
{propertyName
}; 
 865                 propertyNode
.map
["attributes"] = Node
{propertyAttributes
}; 
 866                 propertiesNode
.array
.push_back(propertyNode
); 
 868             ma
->forEachObjCProperty(propertiesVMAddr
, vmAddrConverter
, visitProperty
); 
 869             return propertiesNode
.array
.empty() ? std::optional
<Node
>() : propertiesNode
; 
 872         auto getClassProtocols 
= ^(const dyld3::MachOAnalyzer
* ma
, uint64_t protocolsVMAddr
, 
 873                                    const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) { 
 874             __block Node protocolsNode
; 
 876             auto visitProtocol 
= ^(uint64_t protocolVMAddr
, const dyld3::MachOAnalyzer::ObjCProtocol
& protocol
) { 
 877                 if (const char *name 
= getString(ma
, protocol
.nameVMAddr
)) { 
 878                     protocolsNode
.array
.push_back(Node
{name
}); 
 882             ma
->forEachObjCProtocol(protocolsVMAddr
, vmAddrConverter
, visitProtocol
); 
 884             return protocolsNode
.array
.empty() ? std::optional
<Node
>() : protocolsNode
; 
 887         auto getProtocols 
= ^(const dyld3::MachOAnalyzer
* ma
, 
 888                               const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) { 
 889             __block Node protocols
; 
 891             auto getMethods 
= ^(const dyld3::MachOAnalyzer
* ma
, uint64_t methodListVMAddr
, const std::string 
&prefix
, Node 
&node
){ 
 892                 auto visitMethod 
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) { 
 893                     if (auto name 
= getString(ma
, method
.nameVMAddr
)) { 
 894                         node
.array
.push_back(Node
{prefix 
+ name
}); 
 898                 ma
->forEachObjCMethod(methodListVMAddr
, vmAddrConverter
, visitMethod
); 
 901             auto visitProtocol 
= ^(Diagnostics
& diag
, uint64_t protoVMAddr
, 
 902                                    const dyld3::MachOAnalyzer::ObjCProtocol
& objcProto
) { 
 903                 const char* protoName 
= getString(ma
, objcProto
.nameVMAddr
); 
 908                 entry
.map
["protocolName"] = Node
{protoName
}; 
 910                 if ( objcProto
.protocolsVMAddr 
!= 0 ) { 
 911                     __block Node protocols
; 
 913                     auto visitProtocol 
= ^(uint64_t protocolRefVMAddr
, const dyld3::MachOAnalyzer::ObjCProtocol 
&protocol
) { 
 914                         if (auto name 
= getString(ma
, protocol
.nameVMAddr
)) { 
 915                             protocols
.array
.push_back(Node
{name
}); 
 919                     ma
->forEachObjCProtocol(objcProto
.protocolsVMAddr
, vmAddrConverter
, visitProtocol
); 
 920                     if (!protocols
.array
.empty()) { 
 921                         entry
.map
["protocols"] = protocols
; 
 926                 getMethods(ma
, objcProto
.instanceMethodsVMAddr
, instancePrefix
, methods
); 
 927                 getMethods(ma
, objcProto
.classMethodsVMAddr
, classPrefix
, methods
); 
 928                 if (!methods
.array
.empty()) { 
 929                     entry
.map
["methods"] = methods
; 
 933                 getMethods(ma
, objcProto
.optionalInstanceMethodsVMAddr
, instancePrefix
, optMethods
); 
 934                 getMethods(ma
, objcProto
.optionalClassMethodsVMAddr
, classPrefix
, optMethods
); 
 935                 if (!optMethods
.array
.empty()) { 
 936                     entry
.map
["optionalMethods"] = optMethods
; 
 939                 protocols
.array
.push_back(entry
); 
 943             ma
->forEachObjCProtocol(diag
, vmAddrConverter
, visitProtocol
); 
 945             return protocols
.array
.empty() ? std::optional
<Node
>() : protocols
; 
 948         auto getSelRefs 
= ^(const dyld3::MachOAnalyzer
* ma
, 
 949                             const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) { 
 950             __block 
std::vector
<const char *> selNames
; 
 952             auto visitSelRef 
= ^(uint64_t selRefVMAddr
, uint64_t selRefTargetVMAddr
) { 
 953                 if (auto selValue 
= getString(ma
, selRefTargetVMAddr
)) { 
 954                     selNames
.push_back(selValue
); 
 959             ma
->forEachObjCSelectorReference(diag
, vmAddrConverter
, visitSelRef
); 
 961             std::sort(selNames
.begin(), selNames
.end(), 
 962                       [](const char* a
, const char* b
) { 
 963                 return strcasecmp(a
, b
) < 0; 
 967             for (auto s
: selNames
) { 
 968                 selrefs
.array
.push_back(Node
{s
}); 
 971             return selrefs
.array
.empty() ? std::optional
<Node
>() : selrefs
; 
 974         auto getClasses 
= ^(const dyld3::MachOAnalyzer
* ma
, 
 975                             const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) { 
 977             const uint32_t pointerSize 
= ma
->pointerSize(); 
 979             // Get the vmAddrs for all exported symbols as we want to know if classes 
 981             std::set
<uint64_t> exportedSymbolVMAddrs
; 
 983                 uint64_t loadAddress 
= ma
->preferredLoadAddress(); 
 985                 uint32_t exportTrieRuntimeOffset
; 
 986                 uint32_t exportTrieSize
; 
 987                 if ( ma
->hasExportTrie(exportTrieRuntimeOffset
, exportTrieSize
) ) { 
 988                     const uint8_t* start 
= (uint8_t*)ma 
+ exportTrieRuntimeOffset
; 
 989                     const uint8_t* end 
= start 
+ exportTrieSize
; 
 990                     std::vector
<ExportInfoTrie::Entry
> exports
; 
 991                     if ( ExportInfoTrie::parseTrie(start
, end
, exports
) ) { 
 992                         for (const ExportInfoTrie::Entry
& entry
: exports
) { 
 993                             exportedSymbolVMAddrs
.insert(loadAddress 
+ entry
.info
.address
); 
 999             __block Node classesNode
; 
1000             __block 
bool skippedPreviousClass 
= false; 
1001             auto visitClass 
= ^(Diagnostics
& diag
, uint64_t classVMAddr
, 
1002                                 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, 
1003                                 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) { 
1005                     if (skippedPreviousClass
) { 
1006                         // If the class was bad, then skip the meta class too 
1007                         skippedPreviousClass 
= false; 
1011                     skippedPreviousClass 
= true; 
1014                 std::string classType 
= "-"; 
1017                 dyld3::MachOAnalyzer::PrintableStringResult classNameResult
; 
1018                 const char* className 
= ma
->getPrintableString(objcClass
.nameVMAddr(pointerSize
), classNameResult
); 
1019                 if (classNameResult 
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
) { 
1023                 const char* superClassName 
= nullptr; 
1024                 if ( ma
->inDyldCache() ) { 
1025                     if ( objcClass
.superclassVMAddr 
!= 0 ) { 
1027                             // If we are root class, then our superclass should actually point to our own class 
1028                             const uint32_t RO_ROOT 
= (1<<1); 
1029                             if ( objcClass
.flags(pointerSize
) & RO_ROOT 
) { 
1030                                 auto it 
= classVMAddrToName
.find(objcClass
.superclassVMAddr
); 
1031                                 assert(it 
!= classVMAddrToName
.end()); 
1032                                 superClassName 
= it
->second
; 
1034                                 auto it 
= metaclassVMAddrToName
.find(objcClass
.superclassVMAddr
); 
1035                                 assert(it 
!= metaclassVMAddrToName
.end()); 
1036                                 superClassName 
= it
->second
; 
1039                             auto it 
= classVMAddrToName
.find(objcClass
.superclassVMAddr
); 
1040                             assert(it 
!= classVMAddrToName
.end()); 
1041                             superClassName 
= it
->second
; 
1045                     // On-disk binary.  Lets crack the chain to work out what we are pointing at 
1046                     dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup
; 
1047                     fixup
.raw64 
= objcClass
.superclassVMAddr
; 
1048                     if (fixup
.arm64e
.bind
.bind
) { 
1049                         uint64_t bindOrdinal 
= fixup
.arm64e
.authBind
.auth 
? fixup
.arm64e
.authBind
.ordinal 
: fixup
.arm64e
.bind
.ordinal
; 
1050                         // Bind to another image.  Use the bind table to work out which name to bind to 
1051                         const char* symbolName 
= onDiskChainedFixupBindTargets
[bindOrdinal
]; 
1053                             if ( strstr(symbolName
, "_OBJC_METACLASS_$_") == symbolName 
) { 
1054                                 superClassName 
= symbolName 
+ strlen("_OBJC_METACLASS_$_"); 
1056                                 // Swift classes don't start with these prefixes so just skip them 
1057                                 if (objcClass
.isSwiftLegacy 
|| objcClass
.isSwiftStable
) 
1061                             if ( strstr(symbolName
, "_OBJC_CLASS_$_") == symbolName 
) { 
1062                                 superClassName 
= symbolName 
+ strlen("_OBJC_CLASS_$_"); 
1064                                 // Swift classes don't start with these prefixes so just skip them 
1065                                 if (objcClass
.isSwiftLegacy 
|| objcClass
.isSwiftStable
) 
1070                         // Rebase within this image. 
1072                             auto it 
= onDiskMetaclassVMAddrToName
.find(objcClass
.superclassVMAddr
); 
1073                             assert(it 
!= onDiskMetaclassVMAddrToName
.end()); 
1074                             superClassName 
= it
->second
; 
1076                             auto it 
= onDiskClassVMAddrToName
.find(objcClass
.superclassVMAddr
); 
1077                             assert(it 
!= onDiskClassVMAddrToName
.end()); 
1078                             superClassName 
= it
->second
; 
1083                 // Print the methods on this class 
1084                 __block Node methodsNode
; 
1085                 auto visitMethod 
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) { 
1086                     dyld3::MachOAnalyzer::PrintableStringResult methodNameResult
; 
1087                     const char* methodName 
= ma
->getPrintableString(method
.nameVMAddr
, methodNameResult
); 
1088                     if (methodNameResult 
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
) 
1090                     methodsNode
.array
.push_back(Node
{classType 
+ methodName
}); 
1092                 ma
->forEachObjCMethod(objcClass
.baseMethodsVMAddr(pointerSize
), vmAddrConverter
, 
1095                 std::optional
<Node
> properties 
= getProperties(ma
, objcClass
.basePropertiesVMAddr(pointerSize
), vmAddrConverter
); 
1098                     assert(!classesNode
.array
.empty()); 
1099                     Node
& currentClassNode 
= classesNode
.array
.back(); 
1100                     assert(currentClassNode
.map
["className"].value 
== className
); 
1101                     if (!methodsNode
.array
.empty()) { 
1102                         Node
& currentMethodsNode 
= currentClassNode
.map
["methods"]; 
1103                         currentMethodsNode
.array
.insert(currentMethodsNode
.array
.end(), 
1104                                                         methodsNode
.array
.begin(), 
1105                                                         methodsNode
.array
.end()); 
1107                     if (properties
.has_value()) { 
1108                         Node
& currentPropertiesNode 
= currentClassNode
.map
["properties"]; 
1109                         currentPropertiesNode
.array
.insert(currentPropertiesNode
.array
.end(), 
1110                                                            properties
->array
.begin(), 
1111                                                            properties
->array
.end()); 
1116                 Node currentClassNode
; 
1117                 currentClassNode
.map
["className"] = Node
{className
}; 
1118                 if ( superClassName 
!= nullptr ) 
1119                     currentClassNode
.map
["superClassName"] = Node
{superClassName
}; 
1120                 if (!methodsNode
.array
.empty()) 
1121                     currentClassNode
.map
["methods"] = methodsNode
; 
1122                 if (properties
.has_value()) 
1123                     currentClassNode
.map
["properties"] = properties
.value(); 
1124                 if (std::optional
<Node
> protocols 
= getClassProtocols(ma
, objcClass
.baseProtocolsVMAddr(pointerSize
), vmAddrConverter
)) 
1125                     currentClassNode
.map
["protocols"] = protocols
.value(); 
1127                 currentClassNode
.map
["exported"] = Node
{exportedSymbolVMAddrs
.count(classVMAddr
) != 0}; 
1129                 // We didn't skip this class so mark it as such 
1130                 skippedPreviousClass 
= false; 
1132                 classesNode
.array
.push_back(currentClassNode
); 
1135             ma
->forEachObjCClass(diag
, vmAddrConverter
, visitClass
); 
1136             return classesNode
.array
.empty() ? std::optional
<Node
>() : classesNode
; 
1139         auto getCategories 
= ^(const dyld3::MachOAnalyzer
* ma
, 
1140                                const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) { 
1143             __block Node categoriesNode
; 
1144             auto visitCategory 
= ^(Diagnostics
& diag
, uint64_t categoryVMAddr
, 
1145                                    const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) { 
1146                 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult
; 
1147                 const char* categoryName 
= ma
->getPrintableString(objcCategory
.nameVMAddr
, categoryNameResult
); 
1148                 if (categoryNameResult 
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
) 
1151                     const char* className 
= nullptr; 
1152                     if ( ma
->inDyldCache() ) { 
1153                         auto it 
= classVMAddrToName
.find(objcCategory
.clsVMAddr
); 
1154                         assert(it 
!= classVMAddrToName
.end()); 
1155                         className 
= it
->second
; 
1157                         // On-disk binary.  Lets crack the chain to work out what we are pointing at 
1158                         dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup
; 
1159                         fixup
.raw64 
= objcCategory
.clsVMAddr
; 
1160                         if (fixup
.arm64e
.bind
.bind
) { 
1161                             uint64_t bindOrdinal 
= fixup
.arm64e
.authBind
.auth 
? fixup
.arm64e
.authBind
.ordinal 
: fixup
.arm64e
.bind
.ordinal
; 
1162                             // Bind to another image.  Use the bind table to work out which name to bind to 
1163                             const char* symbolName 
= onDiskChainedFixupBindTargets
[bindOrdinal
]; 
1164                             if ( strstr(symbolName
, "_OBJC_CLASS_$_") == symbolName 
) { 
1165                                 className 
= symbolName 
+ strlen("_OBJC_CLASS_$_"); 
1167                                 // Swift classes don't start with these prefixes so just skip them 
1168                                 // We don't know that this is a Swift class/category though, but skip it anyway 
1172                             auto it 
= onDiskClassVMAddrToName
.find(objcCategory
.clsVMAddr
); 
1173                             if (it 
== onDiskClassVMAddrToName
.end()) { 
1174                                 // This is an odd binary with perhaps a Swift class.  Just skip this entry 
1177                             className 
= it
->second
; 
1181                 // Print the instance methods on this category 
1182                 __block Node methodsNode
; 
1183                 auto visitInstanceMethod 
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) { 
1184                     if (auto methodName 
= getString(ma
, method
.nameVMAddr
)) 
1185                         methodsNode
.array
.push_back(Node
{instancePrefix 
+ methodName
}); 
1187                 ma
->forEachObjCMethod(objcCategory
.instanceMethodsVMAddr
, vmAddrConverter
, 
1188                                       visitInstanceMethod
); 
1190                 // Print the instance methods on this category 
1191                 __block Node classMethodsNode
; 
1192                 auto visitClassMethod 
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) { 
1193                     if (auto methodName 
= getString(ma
, method
.nameVMAddr
)) 
1194                         methodsNode
.array
.push_back(Node
{classPrefix 
+ methodName
}); 
1196                 ma
->forEachObjCMethod(objcCategory
.classMethodsVMAddr
, vmAddrConverter
, 
1199                 Node currentCategoryNode
; 
1200                 currentCategoryNode
.map
["categoryName"] = Node
{categoryName
}; 
1201                 currentCategoryNode
.map
["className"] = Node
{className
}; 
1202                 if (!methodsNode
.array
.empty()) 
1203                     currentCategoryNode
.map
["methods"] = methodsNode
; 
1204                 if (std::optional
<Node
> properties 
= getProperties(ma
, objcCategory
.instancePropertiesVMAddr
, vmAddrConverter
)) 
1205                     currentCategoryNode
.map
["properties"] = properties
.value(); 
1206                 if (std::optional
<Node
> protocols 
= getClassProtocols(ma
, objcCategory
.protocolsVMAddr
, vmAddrConverter
)) 
1207                     currentCategoryNode
.map
["protocols"] = protocols
.value(); 
1209                 categoriesNode
.array
.push_back(currentCategoryNode
); 
1212             ma
->forEachObjCCategory(diag
, vmAddrConverter
, visitCategory
); 
1213             return categoriesNode
.array
.empty() ? std::optional
<Node
>() : categoriesNode
; 
1216         __block 
bool needsComma 
= false; 
1218         dyld3::json::streamArrayBegin(needsComma
); 
1220         dyldCache
->forEachImage(^(const mach_header 
*mh
, const char *installName
) { 
1221             dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter 
= dyldCache
->makeVMAddrConverter(rebased
); 
1222             const dyld3::MachOAnalyzer
* ma 
= (const dyld3::MachOAnalyzer
*)mh
; 
1225             imageRecord
.map
["imagePath"] = Node
{installName
}; 
1226             imageRecord
.map
["imageType"] = Node
{"cache-dylib"}; 
1227             std::optional
<Node
> classes 
= getClasses(ma
, vmAddrConverter
); 
1228             std::optional
<Node
> categories 
= getCategories(ma
, vmAddrConverter
); 
1229             std::optional
<Node
> protocols 
= getProtocols(ma
, vmAddrConverter
); 
1230             std::optional
<Node
> selrefs 
= getSelRefs(ma
, vmAddrConverter
); 
1232             // Skip emitting images with no objc data 
1233             if (!classes
.has_value() && !categories
.has_value() && !protocols
.has_value() && !selrefs
.has_value()) 
1235             if (classes
.has_value()) 
1236                 imageRecord
.map
["classes"] = classes
.value(); 
1237             if (categories
.has_value()) 
1238                 imageRecord
.map
["categories"] = categories
.value(); 
1239             if (protocols
.has_value()) 
1240                 imageRecord
.map
["protocols"] = protocols
.value(); 
1241             if (selrefs
.has_value()) 
1242                 imageRecord
.map
["selrefs"] = selrefs
.value(); 
1244             dyld3::json::streamArrayNode(needsComma
, imageRecord
); 
1247         FileSystemPhysical fileSystem
; 
1248         dyld3::Platform            platform 
= dyldCache
->platform(); 
1249         const dyld3::GradedArchs
&  archs    
= dyld3::GradedArchs::forName(dyldCache
->archName(), true); 
1251         dyldCache
->forEachLaunchClosure(^(const char *executableRuntimePath
, const dyld3::closure::LaunchClosure 
*closure
) { 
1253             char realerPath
[MAXPATHLEN
]; 
1254             dyld3::closure::LoadedFileInfo loadedFileInfo 
= dyld3::MachOAnalyzer::load(diag
, fileSystem
, executableRuntimePath
, archs
, platform
, realerPath
); 
1255             const dyld3::MachOAnalyzer
* ma 
= (const dyld3::MachOAnalyzer
*)loadedFileInfo
.fileContent
; 
1256             uint32_t pointerSize 
= ma
->pointerSize(); 
1258             // Populate the bind targets for classes from other images 
1259             onDiskChainedFixupBindTargets
.clear(); 
1260             ma
->forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) { 
1261                 onDiskChainedFixupBindTargets
.push_back(symbolName
); 
1263             if ( diag
.hasError() ) 
1266             // Populate the rebase targets for class names 
1267             onDiskMetaclassVMAddrToName
.clear(); 
1268             onDiskClassVMAddrToName
.clear(); 
1269             auto visitClass 
= ^(Diagnostics
& diag
, uint64_t classVMAddr
, 
1270                                 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, 
1271                                 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) { 
1272                 if (auto className 
= getString(ma
, objcClass
.nameVMAddr(pointerSize
))) { 
1274                         onDiskMetaclassVMAddrToName
[classVMAddr
] = className
; 
1276                         onDiskClassVMAddrToName
[classVMAddr
] = className
; 
1280             // Get a vmAddrConverter for this on-disk binary.  We can't use the shared cache one 
1281             dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter 
= ma
->makeVMAddrConverter(rebased
); 
1283             ma
->forEachObjCClass(diag
, onDiskVMAddrConverter
, visitClass
); 
1286             imageRecord
.map
["imagePath"] = Node
{executableRuntimePath
}; 
1287             imageRecord
.map
["imageType"] = Node
{"executable"}; 
1288             std::optional
<Node
> classes 
= getClasses(ma
, onDiskVMAddrConverter
); 
1289             std::optional
<Node
> categories 
= getCategories(ma
, onDiskVMAddrConverter
); 
1291             std::optional
<Node
> selrefs 
= getSelRefs(ma
, onDiskVMAddrConverter
); 
1293             // Skip emitting images with no objc data 
1294             if (!classes
.has_value() && !categories
.has_value() && !selrefs
.has_value()) 
1296             if (classes
.has_value()) 
1297                 imageRecord
.map
["classes"] = classes
.value(); 
1298             if (categories
.has_value()) 
1299                 imageRecord
.map
["categories"] = categories
.value(); 
1300             if (selrefs
.has_value()) 
1301                 imageRecord
.map
["selrefs"] = selrefs
.value(); 
1303             dyld3::json::streamArrayNode(needsComma
, imageRecord
); 
1306         dyldCache
->forEachDlopenImage(^(const char *runtimePath
, const dyld3::closure::Image 
*image
) { 
1308             char realerPath
[MAXPATHLEN
]; 
1309             dyld3::closure::LoadedFileInfo loadedFileInfo 
= dyld3::MachOAnalyzer::load(diag
, fileSystem
, runtimePath
, archs
, platform
, realerPath
); 
1310             const dyld3::MachOAnalyzer
* ma 
= (const dyld3::MachOAnalyzer
*)loadedFileInfo
.fileContent
; 
1311             uint32_t pointerSize 
= ma
->pointerSize(); 
1313             // Populate the bind targets for classes from other images 
1314             onDiskChainedFixupBindTargets
.clear(); 
1315             ma
->forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) { 
1316                 onDiskChainedFixupBindTargets
.push_back(symbolName
); 
1318             if ( diag
.hasError() ) 
1321             // Populate the rebase targets for class names 
1322             onDiskMetaclassVMAddrToName
.clear(); 
1323             onDiskClassVMAddrToName
.clear(); 
1324             auto visitClass 
= ^(Diagnostics
& diag
, uint64_t classVMAddr
, 
1325                                 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
, 
1326                                 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) { 
1327                 if (auto className 
= getString(ma
, objcClass
.nameVMAddr(pointerSize
))) { 
1329                         onDiskMetaclassVMAddrToName
[classVMAddr
] = className
; 
1331                         onDiskClassVMAddrToName
[classVMAddr
] = className
; 
1335             // Get a vmAddrConverter for this on-disk binary.  We can't use the shared cache one 
1336             dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter 
= ma
->makeVMAddrConverter(rebased
); 
1338             ma
->forEachObjCClass(diag
, onDiskVMAddrConverter
, visitClass
); 
1341             imageRecord
.map
["imagePath"] = Node
{runtimePath
}; 
1342             imageRecord
.map
["imageType"] = Node
{"non-cache-dylib"}; 
1343             std::optional
<Node
> classes 
= getClasses(ma
, onDiskVMAddrConverter
); 
1344             std::optional
<Node
> categories 
= getCategories(ma
, onDiskVMAddrConverter
); 
1346             std::optional
<Node
> selrefs 
= getSelRefs(ma
, onDiskVMAddrConverter
); 
1348             // Skip emitting images with no objc data 
1349             if (!classes
.has_value() && !categories
.has_value() && !selrefs
.has_value()) 
1351             if (classes
.has_value()) 
1352                 imageRecord
.map
["classes"] = classes
.value(); 
1353             if (categories
.has_value()) 
1354                 imageRecord
.map
["categories"] = categories
.value(); 
1355             if (selrefs
.has_value()) 
1356                 imageRecord
.map
["selrefs"] = selrefs
.value(); 
1358             dyld3::json::streamArrayNode(needsComma
, imageRecord
); 
1361         dyld3::json::streamArrayEnd(needsComma
); 
1363     else if ( options
.mode 
== modeObjCSelectors 
) { 
1364         if ( dyldCache
->objcOpt() == nullptr ) { 
1365             fprintf(stderr
, "Error: could not get optimized objc\n"); 
1368         const objc_opt::objc_selopt_t
* selectors 
= dyldCache
->objcOpt()->selopt(); 
1369         if ( selectors 
== nullptr ) { 
1370             fprintf(stderr
, "Error: could not get optimized objc selectors\n"); 
1374         std::vector
<const char*> selNames
; 
1375         for (uint64_t index 
= 0; index 
!= selectors
->capacity
; ++index
) { 
1376             objc_opt::objc_stringhash_offset_t offset 
= selectors
->offsets()[index
]; 
1379             const char* selName 
= selectors
->getEntryForIndex((uint32_t)index
); 
1380             selNames
.push_back(selName
); 
1383         std::sort(selNames
.begin(), selNames
.end(), 
1384                   [](const char* a
, const char* b
) { 
1385             // Sort by offset, not string value 
1389         dyld3::json::Node root
; 
1390         for (const char* selName 
: selNames
) { 
1391             dyld3::json::Node selNode
; 
1392             selNode
.map
["selectorName"] = dyld3::json::Node
{selName
}; 
1393             selNode
.map
["offset"] = dyld3::json::Node
{(int64_t)selName 
- (int64_t)dyldCache
}; 
1395             root
.array
.push_back(selNode
); 
1398         dyld3::json::printJSON(root
, 0, std::cout
); 
1400     else if ( options
.mode 
== modeExtract 
) { 
1401         return dyld_shared_cache_extract_dylibs(sharedCachePath
, options
.extractionDir
); 
1403     else if ( options
.mode 
== modeObjCImpCaches 
) { 
1404         if (sharedCachePath 
== nullptr) { 
1405             fprintf(stderr
, "Cannot emit imp caches with live cache.  Run again with the path to the cache file\n"); 
1408         __block 
std::map
<uint64_t, const char*> methodToClassMap
; 
1409         __block 
std::map
<uint64_t, const char*> classVMAddrToNameMap
; 
1410         const bool contentRebased 
= false; 
1411         const uint32_t pointerSize 
= 8; 
1413         // Get the base pointers from the magic section in objc 
1414         __block 
uint64_t objcCacheOffsetsSize 
= 0; 
1415         __block 
const void* objcCacheOffsets 
= nullptr; 
1416         __block Diagnostics diag
; 
1417         dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) { 
1418             if ( !strcmp(installName
, "/usr/lib/libobjc.A.dylib") ) { 
1419                 const dyld3::MachOAnalyzer
* ma 
= (const dyld3::MachOAnalyzer
*)mh
; 
1420                 objcCacheOffsets 
= ma
->findSectionContent("__DATA_CONST", "__objc_scoffs", objcCacheOffsetsSize
); 
1424         if ( objcCacheOffsets 
== nullptr ) { 
1425             fprintf(stderr
, "Unable to print imp-caches as cannot find __DATA_CONST __objc_scoffs inside /usr/lib/libobjc.A.dylib\n"); 
1429         if ( objcCacheOffsetsSize 
< (4 * pointerSize
) ) { 
1430             fprintf(stderr
, "Unable to print imp-caches as __DATA_CONST __objc_scoffs is too small (%lld vs required %u)\n", objcCacheOffsetsSize
, (4 * pointerSize
)); 
1434         dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter 
= dyldCache
->makeVMAddrConverter(contentRebased
); 
1436         uint64_t selectorStringVMAddrStart  
= vmAddrConverter
.convertToVMAddr(((uint64_t*)objcCacheOffsets
)[0]); 
1437         uint64_t selectorStringVMAddrEnd    
= vmAddrConverter
.convertToVMAddr(((uint64_t*)objcCacheOffsets
)[1]); 
1439         dyldCache
->forEachImage(^(const mach_header 
*mh
, const char *installName
) { 
1440             if (diag
.hasError()) 
1443             const dyld3::MachOAnalyzer
* ma 
= (const dyld3::MachOAnalyzer
*)mh
; 
1444             intptr_t slide 
= ma
->getSlide(); 
1446             ma
->forEachObjCClass(diag
, vmAddrConverter
, ^(Diagnostics
& diag
, 
1447                                                           uint64_t classVMAddr
, 
1448                                                           uint64_t classSuperclassVMAddr
, 
1449                                                           uint64_t classDataVMAddr
, 
1450                                                           const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, 
1452                 const char* className 
= (const char*)objcClass
.nameVMAddr(pointerSize
) + slide
; 
1453                 classVMAddrToNameMap
[classVMAddr
] = className
; 
1454                 ma
->forEachObjCMethod(objcClass
.baseMethodsVMAddr(pointerSize
), vmAddrConverter
, 
1455                                       ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) { 
1456                     // const char* methodName = (const char*)(method.nameVMAddr + slide); 
1457                     methodToClassMap
[method
.impVMAddr
] = className
; 
1461             ma
->forEachObjCCategory(diag
, vmAddrConverter
, ^(Diagnostics
& diag
, uint64_t categoryVMAddr
, const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) { 
1462                 ma
->forEachObjCMethod(objcCategory
.instanceMethodsVMAddr
, vmAddrConverter
, ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) { 
1463                     const char* catName 
= (const char*)objcCategory
.nameVMAddr 
+ slide
; 
1464                     // const char* methodName = (const char*)(method.nameVMAddr + slide); 
1465                     methodToClassMap
[method
.impVMAddr
] = catName
; 
1468                 ma
->forEachObjCMethod(objcCategory
.classMethodsVMAddr
, vmAddrConverter
, ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) { 
1469                     const char* catName 
= (const char*)objcCategory
.nameVMAddr 
+ slide
; 
1470                     // const char* methodName = (const char*)(method.nameVMAddr + slide); 
1471                     methodToClassMap
[method
.impVMAddr
] = catName
; 
1475         if (diag
.hasError()) 
1478         dyldCache
->forEachImage(^(const mach_header 
*mh
, const char *installName
) { 
1479             if (diag
.hasError()) 
1482             const dyld3::MachOAnalyzer
* ma 
= (const dyld3::MachOAnalyzer
*)mh
; 
1483             intptr_t slide 
= ma
->getSlide(); 
1485             ma
->forEachObjCClass(diag
, vmAddrConverter
, ^(Diagnostics
& diag
, 
1486                                                           uint64_t classVMAddr
, 
1487                                                           uint64_t classSuperclassVMAddr
, 
1488                                                           uint64_t classDataVMAddr
, 
1489                                                           const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, 
1491                 const char* type 
= "class"; 
1493                     type 
= "meta-class"; 
1494                 const char* className 
= (const char*)objcClass
.nameVMAddr(pointerSize
) + slide
; 
1496                 if (objcClass
.methodCacheVMAddr 
== 0) { 
1497                     printf("%s (%s): empty\n", className
, type
); 
1506                     int32_t  fallback_class_offset
; 
1507                     uint32_t cache_shift 
:  5; 
1508                     uint32_t cache_mask  
: 11; 
1509                     uint32_t occupied    
: 14; 
1510                     uint32_t has_inlines 
:  1; 
1511                     uint32_t bit_one     
:  1; 
1512                     struct Bucket buckets
[]; 
1515                 const ImpCache
* impCache 
= (const ImpCache
*)(objcClass
.methodCacheVMAddr 
+ slide
); 
1516                 printf("%s (%s): %d buckets\n", className
, type
, impCache
->cache_mask 
+ 1); 
1518                 if ((classVMAddr 
+ impCache
->fallback_class_offset
) != objcClass
.superclassVMAddr
) { 
1519                     printf("Flattening fallback: %s\n", classVMAddrToNameMap
[classVMAddr 
+ impCache
->fallback_class_offset
]); 
1521                 // Buckets are a 32-bit offset from the impcache itself 
1522                 for (uint32_t i 
= 0; i 
<= impCache
->cache_mask 
; ++i
) { 
1523                     const Bucket
& b 
= impCache
->buckets
[i
]; 
1524                     uint64_t sel 
= (uint64_t)b
.selOffset 
+ selectorStringVMAddrStart
; 
1525                     uint64_t imp 
= classVMAddr 
- (uint64_t)b
.impOffset
; 
1526                     if (b
.selOffset 
== 0xFFFFFFFF) { 
1528                         printf("  - 0x%016llx: %s\n", 0ULL, ""); 
1530                         assert(sel 
< selectorStringVMAddrEnd
); 
1532                         auto it 
= methodToClassMap
.find(imp
); 
1533                         if (it 
== methodToClassMap
.end()) { 
1534                             fprintf(stderr
, "Could not find IMP %llx (for %s)\n", imp
, (const char*)(sel 
+ slide
)); 
1536                         assert(it 
!= methodToClassMap
.end()); 
1537                         printf("  - 0x%016llx: %s (from %s)\n", imp
, (const char*)(sel 
+ slide
), it
->second
); 
1543         switch ( options
.mode 
) { 
1545                 // list all dylibs, including their aliases (symlinks to them) with option vmaddr 
1546                 __block 
std::vector
<std::unordered_set
<std::string
>> indexToPaths
; 
1547                 __block 
std::vector
<uint64_t> indexToAddr
; 
1548                 __block 
std::vector
<std::string
> indexToUUID
; 
1549                 dyldCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const unsigned char* dylibUUID
, const char* installName
, bool& stop
) { 
1550                     std::unordered_set
<std::string
> empty
; 
1551                     if ( options
.printVMAddrs 
) 
1552                         indexToAddr
.push_back(loadAddressUnslid
); 
1553                     if ( options
.printUUIDs 
) { 
1554                         uuid_string_t uuidString
; 
1555                         uuid_unparse_upper(dylibUUID
, uuidString
); 
1556                         indexToUUID
.push_back(uuidString
); 
1558                     indexToPaths
.push_back(empty
); 
1559                     indexToPaths
.back().insert(installName
); 
1561                 dyldCache
->forEachDylibPath(^(const char* dylibPath
, uint32_t index
) { 
1562                     indexToPaths
[index
].insert(dylibPath
); 
1565                 for (const std::unordered_set
<std::string
>& paths 
: indexToPaths
) { 
1566                     for (const std::string
& path
: paths
) { 
1567                         if ( options
.printVMAddrs 
) 
1568                             printf("0x%08llX ", indexToAddr
[index
]); 
1569                         if ( options
.printUUIDs 
) 
1570                              printf("<%s> ", indexToUUID
[index
].c_str()); 
1571                        printf("%s\n", path
.c_str()); 
1577             case modeListDylibsWithSection
: { 
1578                 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) { 
1579                     dyld3::MachOFile
* mf 
= (dyld3::MachOFile
*)mh
; 
1580                     mf
->forEachSection(^(const dyld3::MachOFile::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) { 
1581                         if ( (strcmp(sectInfo
.sectName
, options
.sectionName
) == 0) && (strcmp(sectInfo
.segInfo
.segName
, options
.segmentName
) == 0) ) { 
1582                             printf("%s\n", installName
); 
1590                 __block 
std::map
<uint64_t, const char*> dataSegNames
; 
1591                 __block 
std::map
<uint64_t, uint64_t>    dataSegEnds
; 
1592                 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) { 
1593                     dyld3::MachOFile
* mf 
= (dyld3::MachOFile
*)mh
; 
1594                     mf
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo 
&info
, bool &stop
) { 
1595                         printf("0x%08llX - 0x%08llX %s %s\n", info
.vmAddr
, info
.vmAddr 
+ info
.vmSize
, info
.segName
, installName
); 
1596                         if ( strncmp(info
.segName
, "__DATA", 6) == 0 ) { 
1597                             dataSegNames
[info
.vmAddr
] = installName
; 
1598                             dataSegEnds
[info
.vmAddr
] = info
.vmAddr 
+ info
.vmSize
; 
1602                 // <rdar://problem/51084507> Enhance dyld_shared_cache_util to show where section alignment added padding 
1603                 uint64_t lastEnd 
= 0; 
1604                 for (const auto& entry 
: dataSegEnds
) { 
1605                     uint64_t padding 
= entry
.first 
- lastEnd
; 
1606                     if ( (padding 
> 32) && (lastEnd 
!= 0) ) { 
1607                         printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd
, entry
.first
, padding
/1024); 
1609                     lastEnd 
= entry
.second
; 
1613             case modeDependencies
: { 
1614                 __block 
bool dependentTargetFound 
= false; 
1615                 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) { 
1616                     if ( strcmp(options
.dependentsOfPath
, installName
) != 0 ) 
1618                     dependentTargetFound 
= true; 
1620                     auto printDep 
= [&options
](const char *loadPath
, uint32_t compatVersion
, uint32_t curVersion
) { 
1621                         if ( options
.printDylibVersions 
) { 
1622                             uint32_t compat_vers 
= compatVersion
; 
1623                             uint32_t current_vers 
= curVersion
; 
1624                             printf("\t%s", loadPath
); 
1625                             if ( compat_vers 
!= 0xFFFFFFFF ) { 
1626                                 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n", 
1627                                        (compat_vers 
>> 16), 
1628                                        (compat_vers 
>> 8) & 0xff, 
1629                                        (compat_vers
) & 0xff, 
1630                                        (current_vers 
>> 16), 
1631                                        (current_vers 
>> 8) & 0xff, 
1632                                        (current_vers
) & 0xff); 
1639                             printf("\t%s\n", loadPath
); 
1643                     dyld3::MachOFile
* mf 
= (dyld3::MachOFile
*)mh
; 
1645                     // First print out our dylib and version. 
1646                     const char* dylibInstallName
; 
1647                     uint32_t currentVersion
; 
1648                     uint32_t compatVersion
; 
1649                     if ( mf
->getDylibInstallName(&dylibInstallName
, &compatVersion
, ¤tVersion
) ) { 
1650                         printDep(dylibInstallName
, compatVersion
, currentVersion
); 
1653                     // Then the dependent dylibs. 
1654                     mf
->forEachDependentDylib(^(const char *loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) { 
1655                         printDep(loadPath
, compatVersion
, curVersion
); 
1658                 if (options
.dependentsOfPath 
&& !dependentTargetFound
) { 
1659                     fprintf(stderr
, "Error: could not find '%s' in the shared cache at\n  %s\n", options
.dependentsOfPath
, sharedCachePath
); 
1664             case modeLinkEdit
: { 
1665                 std::map
<uint32_t, const char*> pageToContent
; 
1666                 auto add_linkedit 
= [&pageToContent
](uint32_t pageStart
, uint32_t pageEnd
, const char* message
) { 
1667                     for (uint32_t p 
= pageStart
; p 
<= pageEnd
; p 
+= 4096) { 
1668                         std::map
<uint32_t, const char*>::iterator pos 
= pageToContent
.find(p
); 
1669                         if ( pos 
== pageToContent
.end() ) { 
1670                             pageToContent
[p
] = strdup(message
); 
1673                             const char* oldMessage 
= pos
->second
; 
1675                             asprintf(&newMesssage
, "%s, %s", oldMessage
, message
); 
1676                             pageToContent
[p
] = newMesssage
; 
1677                             ::free((void*)oldMessage
); 
1682                 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) { 
1683                     dyld3::MachOAnalyzer
* ma 
= (dyld3::MachOAnalyzer
*)mh
; 
1685                     dyld3::MachOAnalyzer::LinkEditInfo leInfo
; 
1686                     ma
->getLinkEditPointers(diag
, leInfo
); 
1688                     if (diag
.hasError()) 
1692                     const char* shortName 
= strrchr(installName
, '/') + 1; 
1693                     // add export trie info 
1694                     if ( leInfo
.dyldInfo
->export_size 
!= 0 ) { 
1695                         //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off()); 
1696                         uint32_t exportPageOffsetStart 
= leInfo
.dyldInfo
->export_off 
& (-4096); 
1697                         uint32_t exportPageOffsetEnd 
= (leInfo
.dyldInfo
->export_off 
+ leInfo
.dyldInfo
->export_size
) & (-4096); 
1698                         sprintf(message
, "exports from %s", shortName
); 
1699                         add_linkedit(exportPageOffsetStart
, exportPageOffsetEnd
, message
); 
1702                     if ( leInfo
.dyldInfo
->bind_size 
!= 0 ) { 
1703                         uint32_t bindPageOffsetStart 
= leInfo
.dyldInfo
->bind_off 
& (-4096); 
1704                         uint32_t bindPageOffsetEnd 
= (leInfo
.dyldInfo
->bind_off 
+ leInfo
.dyldInfo
->bind_size
) & (-4096); 
1705                         sprintf(message
, "bindings from %s", shortName
); 
1706                         add_linkedit(bindPageOffsetStart
, bindPageOffsetEnd
, message
); 
1708                     // add lazy binding info 
1709                     if ( leInfo
.dyldInfo
->lazy_bind_size 
!= 0 ) { 
1710                         uint32_t lazybindPageOffsetStart 
= leInfo
.dyldInfo
->lazy_bind_off 
& (-4096); 
1711                         uint32_t lazybindPageOffsetEnd 
= (leInfo
.dyldInfo
->lazy_bind_off 
+ leInfo
.dyldInfo
->lazy_bind_size
) & (-4096); 
1712                         sprintf(message
, "lazy bindings from %s", shortName
); 
1713                         add_linkedit(lazybindPageOffsetStart
, lazybindPageOffsetEnd
, message
); 
1715                     // add weak binding info 
1716                     if ( leInfo
.dyldInfo
->weak_bind_size 
!= 0 ) { 
1717                         uint32_t weakbindPageOffsetStart 
= leInfo
.dyldInfo
->weak_bind_off 
& (-4096); 
1718                         uint32_t weakbindPageOffsetEnd 
= (leInfo
.dyldInfo
->weak_bind_off 
+ leInfo
.dyldInfo
->weak_bind_size
) & (-4096); 
1719                         sprintf(message
, "weak bindings from %s", shortName
); 
1720                         add_linkedit(weakbindPageOffsetStart
, weakbindPageOffsetEnd
, message
); 
1724                 for (std::map
<uint32_t, const char*>::iterator it 
= pageToContent
.begin(); it 
!= pageToContent
.end(); ++it
) { 
1725                     printf("0x%08X %s\n", it
->first
, it
->second
); 
1734                 __block 
std::vector
<TextInfo
> textSegments
; 
1735                 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) { 
1737                     dyld3::MachOAnalyzer
* ma 
= (dyld3::MachOAnalyzer
*)mh
; 
1738                     ma
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo 
&info
, bool &stop
) { 
1739                         if ( strcmp(info
.segName
, "__TEXT") != 0 ) 
1741                         textSegments
.push_back({ info
.fileSize
, installName 
}); 
1744                 std::sort(textSegments
.begin(), textSegments
.end(), [](const TextInfo
& left
, const TextInfo
& right
) { 
1745                     return (left
.textSize 
> right
.textSize
); 
1747                 for (std::vector
<TextInfo
>::iterator it 
= textSegments
.begin(); it 
!= textSegments
.end(); ++it
) { 
1748                     printf(" 0x%08llX  %s\n", it
->textSize
, it
->path
); 
1752             case modePatchTable
: { 
1753                 std::vector
<SegmentInfo
> segInfos
; 
1754                 buildSegmentInfo(dyldCache
, segInfos
); 
1755                 __block 
uint32_t imageIndex 
= 0; 
1756                 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) { 
1757                     printf("%s:\n", installName
); 
1758                     dyldCache
->forEachPatchableExport(imageIndex
, ^(uint32_t cacheOffsetOfImpl
, const char* exportName
) { 
1759                         printf("    export: 0x%08X  %s\n", cacheOffsetOfImpl
, exportName
); 
1760                         dyldCache
->forEachPatchableUseOfExport(imageIndex
, cacheOffsetOfImpl
, ^(dyld_cache_patchable_location patchLocation
) { 
1761                             SegmentInfo usageAt
; 
1762                             const uint64_t patchLocVmAddr 
= dyldCache
->unslidLoadAddress() + patchLocation
.cacheOffset
; 
1763                             findImageAndSegment(dyldCache
, segInfos
, patchLocation
.cacheOffset
, &usageAt
); 
1764                             if ( patchLocation
.addend 
== 0 ) 
1765                                 printf("        used by: %s+0x%04llX in %s\n", usageAt
.segName
, patchLocVmAddr
-usageAt
.vmAddr
, usageAt
.installName
); 
1767                                 printf("        used by: %s+0x%04llX (addend=%d) in %s\n", usageAt
.segName
, patchLocVmAddr
-usageAt
.vmAddr
, patchLocation
.addend
, usageAt
.installName
); 
1777             case modeVerboseSlideInfo
: 
1779             case modeLocalSymbols
: 
1781             case modeJSONDependents
: 
1782             case modeSectionSizes
: 
1784             case modeObjCProtocols
: 
1785             case modeObjCImpCaches
: 
1786             case modeObjCClasses
: 
1787             case modeObjCSelectors
: