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
: