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>
42 #include <TargetConditionals.h>
49 #include "ClosureBuilder.h"
50 #include "DyldSharedCache.h"
51 #include "ClosureFileSystemPhysical.h"
52 #include "JSONWriter.h"
55 #include "objc-shared-cache.h"
58 #define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle"
60 #define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle"
63 using dyld3::closure::ClosureBuilder
;
64 using dyld3::closure::FileSystemPhysical
;
66 // mmap() an shared cache file read/only but laid out like it would be at runtime
67 static const DyldSharedCache
* mapCacheFile(const char* path
)
70 if ( ::stat(path
, &statbuf
) ) {
71 fprintf(stderr
, "Error: stat failed for dyld shared cache at %s\n", path
);
75 int cache_fd
= ::open(path
, O_RDONLY
);
77 fprintf(stderr
, "Error: failed to open shared cache file at %s\n", path
);
81 uint8_t firstPage
[4096];
82 if ( ::pread(cache_fd
, firstPage
, 4096, 0) != 4096 ) {
83 fprintf(stderr
, "Error: failed to read shared cache file at %s\n", path
);
86 const dyld_cache_header
* header
= (dyld_cache_header
*)firstPage
;
87 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)(firstPage
+ header
->mappingOffset
);
89 size_t vmSize
= (size_t)(mappings
[2].address
+ mappings
[2].size
- mappings
[0].address
);
91 kern_return_t r
= ::vm_allocate(mach_task_self(), &result
, vmSize
, VM_FLAGS_ANYWHERE
);
92 if ( r
!= KERN_SUCCESS
) {
93 fprintf(stderr
, "Error: failed to allocate space to load shared cache file at %s\n", path
);
96 for (int i
=0; i
< 3; ++i
) {
97 void* mapped_cache
= ::mmap((void*)(result
+ mappings
[i
].address
- mappings
[0].address
), (size_t)mappings
[i
].size
,
98 PROT_READ
, MAP_FIXED
| MAP_PRIVATE
, cache_fd
, mappings
[i
].fileOffset
);
99 if (mapped_cache
== MAP_FAILED
) {
100 fprintf(stderr
, "Error: mmap() for shared cache at %s failed, errno=%d\n", path
, errno
);
106 return (DyldSharedCache
*)result
;
115 modeVerboseSlideInfo
,
134 const char* dependentsOfPath
;
135 const char* extractionDir
;
138 bool printDylibVersions
;
144 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");
147 static void checkMode(Mode mode
) {
148 if ( mode
!= modeNone
) {
149 fprintf(stderr
, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n");
155 static bool isAlias(const char* path
, const DyldSharedCache
* dyldCache
) {
156 const dyld_cache_header
* header
= &dyldCache
->header
;
157 const dyld_cache_mapping_info
* mappings
= (const dyld_cache_mapping_info
*)((char*)dyldCache
+ header
->mappingOffset
);
158 const dyld_cache_mapping_info
* textMapping
= &mappings
[0];
159 // paths for aliases are store between cache header and first segment
160 return path
< (char*)textMapping
;
163 int main (int argc
, const char* argv
[]) {
165 const char* sharedCachePath
= nullptr;
168 options
.mode
= modeNone
;
169 options
.printUUIDs
= false;
170 options
.printVMAddrs
= false;
171 options
.printDylibVersions
= false;
172 options
.printInodes
= false;
173 options
.dependentsOfPath
= NULL
;
174 options
.extractionDir
= NULL
;
176 bool printStrings
= false;
177 bool printExports
= false;
179 for (uint32_t i
= 1; i
< argc
; i
++) {
180 const char* opt
= argv
[i
];
182 if (strcmp(opt
, "-list") == 0) {
183 checkMode(options
.mode
);
184 options
.mode
= modeList
;
186 else if (strcmp(opt
, "-dependents") == 0) {
187 checkMode(options
.mode
);
188 options
.mode
= modeDependencies
;
189 options
.dependentsOfPath
= argv
[++i
];
191 fprintf(stderr
, "Error: option -depdendents requires an argument\n");
196 else if (strcmp(opt
, "-linkedit") == 0) {
197 checkMode(options
.mode
);
198 options
.mode
= modeLinkEdit
;
200 else if (strcmp(opt
, "-info") == 0) {
201 checkMode(options
.mode
);
202 options
.mode
= modeInfo
;
204 else if (strcmp(opt
, "-slide_info") == 0) {
205 checkMode(options
.mode
);
206 options
.mode
= modeSlideInfo
;
208 else if (strcmp(opt
, "-verbose_slide_info") == 0) {
209 checkMode(options
.mode
);
210 options
.mode
= modeVerboseSlideInfo
;
212 else if (strcmp(opt
, "-accelerator_info") == 0) {
213 checkMode(options
.mode
);
214 options
.mode
= modeAcceleratorInfo
;
216 else if (strcmp(opt
, "-text_info") == 0) {
217 checkMode(options
.mode
);
218 options
.mode
= modeTextInfo
;
220 else if (strcmp(opt
, "-local_symbols") == 0) {
221 checkMode(options
.mode
);
222 options
.mode
= modeLocalSymbols
;
224 else if (strcmp(opt
, "-strings") == 0) {
225 if (options
.mode
!= modeStrings
)
226 checkMode(options
.mode
);
227 options
.mode
= modeStrings
;
230 else if (strcmp(opt
, "-sections") == 0) {
231 checkMode(options
.mode
);
232 options
.mode
= modeSectionSizes
;
234 else if (strcmp(opt
, "-exports") == 0) {
235 if (options
.mode
!= modeStrings
)
236 checkMode(options
.mode
);
237 options
.mode
= modeStrings
;
240 else if (strcmp(opt
, "-map") == 0) {
241 checkMode(options
.mode
);
242 options
.mode
= modeMap
;
244 else if (strcmp(opt
, "-json-map") == 0) {
245 checkMode(options
.mode
);
246 options
.mode
= modeJSONMap
;
248 else if (strcmp(opt
, "-json-dependents") == 0) {
249 checkMode(options
.mode
);
250 options
.mode
= modeJSONDependents
;
252 else if (strcmp(opt
, "-size") == 0) {
253 checkMode(options
.mode
);
254 options
.mode
= modeSize
;
256 else if (strcmp(opt
, "-objc-protocols") == 0) {
257 checkMode(options
.mode
);
258 options
.mode
= modeObjCProtocols
;
260 else if (strcmp(opt
, "-objc-classes") == 0) {
261 checkMode(options
.mode
);
262 options
.mode
= modeObjCClasses
;
264 else if (strcmp(opt
, "-objc-selectors") == 0) {
265 checkMode(options
.mode
);
266 options
.mode
= modeObjCSelectors
;
268 else if (strcmp(opt
, "-extract") == 0) {
269 checkMode(options
.mode
);
270 options
.mode
= modeExtract
;
271 options
.extractionDir
= argv
[++i
];
273 fprintf(stderr
, "Error: option -extract requires a directory argument\n");
278 else if (strcmp(opt
, "-uuid") == 0) {
279 options
.printUUIDs
= true;
281 else if (strcmp(opt
, "-inode") == 0) {
282 options
.printInodes
= true;
284 else if (strcmp(opt
, "-versions") == 0) {
285 options
.printDylibVersions
= true;
287 else if (strcmp(opt
, "-vmaddr") == 0) {
288 options
.printVMAddrs
= true;
291 fprintf(stderr
, "Error: unrecognized option %s\n", opt
);
297 sharedCachePath
= opt
;
301 if ( options
.mode
== modeNone
) {
302 fprintf(stderr
, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
307 if ( options
.mode
!= modeSlideInfo
&& options
.mode
!= modeVerboseSlideInfo
) {
308 if ( options
.printUUIDs
&& (options
.mode
!= modeList
) )
309 fprintf(stderr
, "Warning: -uuid option ignored outside of -list mode\n");
311 if ( options
.printVMAddrs
&& (options
.mode
!= modeList
) )
312 fprintf(stderr
, "Warning: -vmaddr option ignored outside of -list mode\n");
314 if ( options
.printDylibVersions
&& (options
.mode
!= modeDependencies
) )
315 fprintf(stderr
, "Warning: -versions option ignored outside of -dependents mode\n");
317 if ( (options
.mode
== modeDependencies
) && (options
.dependentsOfPath
== NULL
) ) {
318 fprintf(stderr
, "Error: -dependents given, but no dylib path specified\n");
324 const DyldSharedCache
* dyldCache
= nullptr;
325 if ( sharedCachePath
!= nullptr ) {
326 dyldCache
= mapCacheFile(sharedCachePath
);
327 // mapCacheFile prints an error if something goes wrong, so just return in that case.
328 if ( dyldCache
== nullptr )
332 #if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
334 dyldCache
= (DyldSharedCache
*)_dyld_get_shared_cache_range(&cacheLength
);
336 if (dyldCache
== nullptr) {
337 fprintf(stderr
, "Could not get in-memory shared cache\n");
340 if ( options
.mode
== modeObjCClasses
) {
341 fprintf(stderr
, "Cannot use -objc-info with a live cache. Please run with a path to an on-disk cache file\n");
346 if ( options
.mode
== modeSlideInfo
|| options
.mode
== modeVerboseSlideInfo
) {
347 const dyld_cache_header
* header
= &dyldCache
->header
;
348 if ( header
->slideInfoOffset
== 0 ) {
349 fprintf(stderr
, "Error: dyld shared cache does not contain slide info\n");
352 const dyld_cache_mapping_info
* mappings
= (const dyld_cache_mapping_info
*)((char*)dyldCache
+ header
->mappingOffset
);
353 const dyld_cache_mapping_info
* dataMapping
= &mappings
[1];
354 uint64_t dataStartAddress
= dataMapping
->address
;
355 uint64_t dataSize
= dataMapping
->size
;
357 const dyld_cache_slide_info
* slideInfoHeader
= dyldCache
->slideInfo();
358 printf("slide info version=%d\n", slideInfoHeader
->version
);
359 if ( slideInfoHeader
->version
== 1 ) {
360 printf("toc_count=%d, data page count=%lld\n", slideInfoHeader
->toc_count
, dataSize
/4096);
361 const dyld_cache_slide_info_entry
* entries
= (dyld_cache_slide_info_entry
*)((char*)slideInfoHeader
+ slideInfoHeader
->entries_offset
);
362 const uint16_t* tocs
= (uint16_t*)((char*)slideInfoHeader
+ slideInfoHeader
->toc_offset
);
363 for(int i
=0; i
< slideInfoHeader
->toc_count
; ++i
) {
364 printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress
+ i
*4096, i
, tocs
[i
]);
365 const dyld_cache_slide_info_entry
* entry
= &entries
[tocs
[i
]];
366 for(int j
=0; j
< slideInfoHeader
->entries_size
; ++j
)
367 printf("%02X", entry
->bits
[j
]);
371 else if ( slideInfoHeader
->version
== 2 ) {
372 const dyld_cache_slide_info2
* slideInfo
= (dyld_cache_slide_info2
*)(slideInfoHeader
);
373 printf("page_size=%d\n", slideInfo
->page_size
);
374 printf("delta_mask=0x%016llX\n", slideInfo
->delta_mask
);
375 printf("value_add=0x%016llX\n", slideInfo
->value_add
);
376 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo
->page_starts_count
, slideInfo
->page_extras_count
);
377 const uint16_t* starts
= (uint16_t* )((char*)slideInfo
+ slideInfo
->page_starts_offset
);
378 const uint16_t* extras
= (uint16_t* )((char*)slideInfo
+ slideInfo
->page_extras_offset
);
379 for (int i
=0; i
< slideInfo
->page_starts_count
; ++i
) {
380 const uint16_t start
= starts
[i
];
381 auto rebaseChain
= [&](uint8_t* pageContent
, uint16_t startOffset
)
383 uintptr_t slideAmount
= 0;
384 const uintptr_t deltaMask
= (uintptr_t)(slideInfo
->delta_mask
);
385 const uintptr_t valueMask
= ~deltaMask
;
386 const uintptr_t valueAdd
= (uintptr_t)(slideInfo
->value_add
);
387 const unsigned deltaShift
= __builtin_ctzll(deltaMask
) - 2;
389 uint32_t pageOffset
= startOffset
;
391 while ( delta
!= 0 ) {
392 uint8_t* loc
= pageContent
+ pageOffset
;
393 uintptr_t rawValue
= *((uintptr_t*)loc
);
394 delta
= (uint32_t)((rawValue
& deltaMask
) >> deltaShift
);
395 uintptr_t value
= (rawValue
& valueMask
);
398 value
+= slideAmount
;
400 printf(" [% 5d + 0x%04llX]: 0x%016llX = 0x%016llX\n", i
, (uint64_t)(pageOffset
), (uint64_t)rawValue
, (uint64_t)value
);
404 const uint8_t* dataPagesStart
= dyldCache
->dataRegionStart();
405 if ( start
== DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE
) {
406 printf("page[% 5d]: no rebasing\n", i
);
408 else if ( start
& DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
) {
409 printf("page[% 5d]: ", i
);
410 int j
=(start
& 0x3FFF);
413 uint16_t aStart
= extras
[j
];
414 printf("start=0x%04X ", aStart
& 0x3FFF);
415 if ( options
.mode
== modeVerboseSlideInfo
) {
416 uint8_t* page
= (uint8_t*)(long)(dataPagesStart
+ (slideInfo
->page_size
*i
));
417 uint16_t pageStartOffset
= (aStart
& 0x3FFF)*4;
418 rebaseChain(page
, pageStartOffset
);
420 done
= (extras
[j
] & DYLD_CACHE_SLIDE_PAGE_ATTR_END
);
426 printf("page[% 5d]: start=0x%04X\n", i
, starts
[i
]);
427 if ( options
.mode
== modeVerboseSlideInfo
) {
428 uint8_t* page
= (uint8_t*)(long)(dataPagesStart
+ (slideInfo
->page_size
*i
));
429 uint16_t pageStartOffset
= start
*4;
430 rebaseChain(page
, pageStartOffset
);
435 else if ( slideInfoHeader
->version
== 3 ) {
436 const dyld_cache_slide_info3
* slideInfo
= (dyld_cache_slide_info3
*)(slideInfoHeader
);
437 printf("page_size=%d\n", slideInfo
->page_size
);
438 printf("page_starts_count=%d\n", slideInfo
->page_starts_count
);
439 printf("auth_value_add=0x%016llX\n", slideInfo
->auth_value_add
);
440 const uintptr_t authValueAdd
= (uintptr_t)(slideInfo
->auth_value_add
);
441 const uint8_t* dataSegmentStart
= dyldCache
->dataRegionStart();
442 for (int i
=0; i
< slideInfo
->page_starts_count
; ++i
) {
443 uint16_t delta
= slideInfo
->page_starts
[i
];
444 if ( delta
== DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
) {
445 printf("page[% 5d]: no rebasing\n", i
);
449 printf("page[% 5d]: start=0x%04X\n", i
, delta
);
450 if ( options
.mode
!= modeVerboseSlideInfo
)
453 delta
= delta
/sizeof(uint64_t); // initial offset is byte based
454 const uint8_t* pageStart
= dataSegmentStart
+ (i
* slideInfo
->page_size
);
455 const dyld_cache_slide_pointer3
* loc
= (dyld_cache_slide_pointer3
*)pageStart
;
458 delta
= loc
->plain
.offsetToNextPointer
;
459 dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr
;
460 ptr
.raw64
= *((uint64_t*)loc
);
461 if ( loc
->auth
.authenticated
) {
462 uint64_t target
= authValueAdd
+ loc
->auth
.offsetFromSharedCacheBase
;
463 uint64_t targetValue
= ptr
.arm64e
.signPointer((void*)loc
, target
);
464 printf(" [% 5d + 0x%04llX]: 0x%016llX (JOP: diversity %d, address %s, %s)\n",
465 i
, (uint64_t)((const uint8_t*)loc
- pageStart
), targetValue
,
466 ptr
.arm64e
.authBind
.diversity
, ptr
.arm64e
.authBind
.addrDiv
? "true" : "false",
467 ptr
.arm64e
.keyName());
470 uint64_t targetValue
= ptr
.arm64e
.unpackTarget();
471 printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i
, (uint64_t)((const uint8_t*)loc
- pageStart
), targetValue
);
473 } while (delta
!= 0);
476 else if ( slideInfoHeader
->version
== 4 ) {
477 const dyld_cache_slide_info4
* slideInfo
= (dyld_cache_slide_info4
*)(slideInfoHeader
);
478 printf("page_size=%d\n", slideInfo
->page_size
);
479 printf("delta_mask=0x%016llX\n", slideInfo
->delta_mask
);
480 printf("value_add=0x%016llX\n", slideInfo
->value_add
);
481 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo
->page_starts_count
, slideInfo
->page_extras_count
);
482 const uint16_t* starts
= (uint16_t* )((char*)slideInfo
+ slideInfo
->page_starts_offset
);
483 const uint16_t* extras
= (uint16_t* )((char*)slideInfo
+ slideInfo
->page_extras_offset
);
484 for (int i
=0; i
< slideInfo
->page_starts_count
; ++i
) {
485 const uint16_t start
= starts
[i
];
486 auto rebaseChainV4
= [&](uint8_t* pageContent
, uint16_t startOffset
)
488 uintptr_t slideAmount
= 0;
489 const uintptr_t deltaMask
= (uintptr_t)(slideInfo
->delta_mask
);
490 const uintptr_t valueMask
= ~deltaMask
;
491 const uintptr_t valueAdd
= (uintptr_t)(slideInfo
->value_add
);
492 const unsigned deltaShift
= __builtin_ctzll(deltaMask
) - 2;
494 uint32_t pageOffset
= startOffset
;
496 while ( delta
!= 0 ) {
497 uint8_t* loc
= pageContent
+ pageOffset
;
498 uint32_t rawValue
= *((uint32_t*)loc
);
499 delta
= (uint32_t)((rawValue
& deltaMask
) >> deltaShift
);
500 uintptr_t value
= (rawValue
& valueMask
);
501 if ( (value
& 0xFFFF8000) == 0 ) {
502 // small positive non-pointer, use as-is
504 else if ( (value
& 0x3FFF8000) == 0x3FFF8000 ) {
505 // small negative non-pointer
510 value
+= slideAmount
;
512 printf(" [% 5d + 0x%04X]: 0x%08X\n", i
, pageOffset
, rawValue
);
516 const uint8_t* dataPagesStart
= dyldCache
->dataRegionStart();
517 if ( start
== DYLD_CACHE_SLIDE4_PAGE_NO_REBASE
) {
518 printf("page[% 5d]: no rebasing\n", i
);
520 else if ( start
& DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA
) {
521 printf("page[% 5d]: ", i
);
522 int j
=(start
& DYLD_CACHE_SLIDE4_PAGE_INDEX
);
525 uint16_t aStart
= extras
[j
];
526 printf("start=0x%04X ", aStart
& DYLD_CACHE_SLIDE4_PAGE_INDEX
);
527 if ( options
.mode
== modeVerboseSlideInfo
) {
528 uint8_t* page
= (uint8_t*)(long)(dataPagesStart
+ (slideInfo
->page_size
*i
));
529 uint16_t pageStartOffset
= (aStart
& DYLD_CACHE_SLIDE4_PAGE_INDEX
)*4;
530 rebaseChainV4(page
, pageStartOffset
);
532 done
= (extras
[j
] & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END
);
538 printf("page[% 5d]: start=0x%04X\n", i
, starts
[i
]);
539 if ( options
.mode
== modeVerboseSlideInfo
) {
540 uint8_t* page
= (uint8_t*)(long)(dataPagesStart
+ (slideInfo
->page_size
*i
));
541 uint16_t pageStartOffset
= start
*4;
542 rebaseChainV4(page
, pageStartOffset
);
548 else if ( options
.mode
== modeInfo
) {
549 const dyld_cache_header
* header
= &dyldCache
->header
;
551 if ( header
->mappingOffset
>= 0x68 ) {
552 const uint8_t* uuid
= header
->uuid
;
553 printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
554 uuid
[0], uuid
[1], uuid
[2], uuid
[3],
555 uuid
[4], uuid
[5], uuid
[6], uuid
[7],
556 uuid
[8], uuid
[9], uuid
[10], uuid
[11],
557 uuid
[12], uuid
[13], uuid
[14], uuid
[15]);
562 if ( header
->mappingOffset
>= 0xE0 ) {
563 // HACK until this uses new header
564 uint32_t platform
= *((uint32_t*)(((char*)header
) + 0xD8));
565 uint32_t bitfield
= *((uint32_t*)(((char*)header
) + 0xDC));
566 uint32_t simulator
= bitfield
& 0x200;
567 uint32_t locallyBuiltCache
= bitfield
& 0x400;
570 printf("platform: macOS\n");
574 printf("platform: iOS simulator\n");
576 printf("platform: iOS\n");
580 printf("platform: tvOS simulator\n");
582 printf("platform: tvOS\n");
586 printf("platform: watchOS simulator\n");
588 printf("platform: watchOS\n");
591 printf("platform: bridgeOS\n");
594 printf("platform: 0x%08X 0x%08X\n", platform
, simulator
);
596 printf("built by: %s\n", locallyBuiltCache
? "local machine" : "B&I");
598 printf("cache type: %s\n", header
->cacheType
? "production" : "development");
599 printf("image count: %u\n", header
->imagesCount
);
600 if ( (header
->mappingOffset
>= 0x78) && (header
->branchPoolsOffset
!= 0) ) {
601 printf("branch pool count: %u\n", header
->branchPoolsCount
);
603 if ( header
->slideInfoSize
> 0 ) {
604 uint32_t pageSize
= 0x4000; // fix me for intel
605 uint32_t possibleSlideValues
= (uint32_t)(header
->maxSlide
/pageSize
);
606 uint32_t entropyBits
= 32 - __builtin_clz(possibleSlideValues
- 1);
607 printf("ASLR entropy: %u-bits\n", entropyBits
);
609 printf("mappings:\n");
610 const dyld_cache_mapping_info
* mappings
= (const dyld_cache_mapping_info
*)((char*)dyldCache
+ header
->mappingOffset
);
611 for (uint32_t i
=0; i
< header
->mappingCount
; ++i
) {
612 if ( mappings
[i
].initProt
& VM_PROT_EXECUTE
)
613 printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
614 mappings
[i
].size
/(1024*1024), mappings
[i
].fileOffset
, mappings
[i
].fileOffset
+ mappings
[i
].size
,
615 mappings
[i
].address
, mappings
[i
].address
+ mappings
[i
].size
);
616 else if ( mappings
[i
].initProt
& VM_PROT_WRITE
)
617 printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
618 mappings
[i
].size
/(1024*1024), mappings
[i
].fileOffset
, mappings
[i
].fileOffset
+ mappings
[i
].size
,
619 mappings
[i
].address
, mappings
[i
].address
+ mappings
[i
].size
);
620 else if ( mappings
[i
].initProt
& VM_PROT_READ
)
621 printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
622 mappings
[i
].size
/(1024*1024), mappings
[i
].fileOffset
, mappings
[i
].fileOffset
+ mappings
[i
].size
,
623 mappings
[i
].address
, mappings
[i
].address
+ mappings
[i
].size
);
625 if ( header
->codeSignatureOffset
!= 0 ) {
626 uint64_t size
= header
->codeSignatureSize
;
627 uint64_t csAddr
= mappings
[header
->mappingCount
-1].address
+ mappings
[header
->mappingCount
-1].size
;
629 printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
630 size
/(1024*1024), header
->codeSignatureOffset
, header
->codeSignatureOffset
+ size
, csAddr
, csAddr
+ size
);
632 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
633 header
->slideInfoSize
/1024, header
->slideInfoOffset
, header
->slideInfoOffset
+ header
->slideInfoSize
);
634 if ( header
->localSymbolsOffset
!= 0 )
635 printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
636 header
->localSymbolsSize
/(1024*1024), header
->localSymbolsOffset
, header
->localSymbolsOffset
+ header
->localSymbolsSize
);
637 if ( (header
->mappingOffset
>= 0x78) && (header
->accelerateInfoSize
!= 0) )
638 printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n",
639 header
->accelerateInfoSize
/1024, header
->accelerateInfoAddr
, header
->accelerateInfoAddr
+ header
->accelerateInfoSize
);
641 else if ( options
.mode
== modeAcceleratorInfo
) {
642 const dyld_cache_header
* header
= &dyldCache
->header
;
643 if ( (header
->mappingOffset
< sizeof(dyld_cache_header
)) || (header
->accelerateInfoSize
== 0) ) {
644 printf("no accelerator info\n");
647 const dyld_cache_mapping_info
* mappings
= (const dyld_cache_mapping_info
*)((char*)dyldCache
+ header
->mappingOffset
);
648 uint64_t aiAddr
= header
->accelerateInfoAddr
;
649 const dyld_cache_accelerator_info
* accelInfo
= NULL
;
650 for (uint32_t i
=0; i
< header
->mappingCount
; ++i
) {
651 if ( (mappings
[i
].address
<= aiAddr
) && (aiAddr
< mappings
[i
].address
+mappings
[i
].size
) ) {
652 uint64_t offset
= aiAddr
- mappings
[i
].address
+ mappings
[i
].fileOffset
;
653 accelInfo
= (dyld_cache_accelerator_info
*)((uint8_t*)dyldCache
+ offset
);
656 if ( accelInfo
== NULL
) {
657 printf("accelerator info not in any mapped range\n");
660 const dyld_cache_image_info
* images
= (dyld_cache_image_info
*)((char*)dyldCache
+ header
->imagesOffset
);
661 const dyld_cache_image_info_extra
* imagesExtra
= (dyld_cache_image_info_extra
*)((char*)accelInfo
+ accelInfo
->imagesExtrasOffset
);
662 const uint16_t* dependencyArray
= (uint16_t*)((char*)accelInfo
+ accelInfo
->depListOffset
);
663 const uint16_t* reExportArray
= (uint16_t*)((char*)accelInfo
+ accelInfo
->reExportListOffset
);
664 printf("extra image info (count=%u):\n", accelInfo
->imageExtrasCount
);
665 for (uint32_t i
=0; i
< accelInfo
->imageExtrasCount
; ++i
) {
666 printf(" image[%3u] %s:\n", i
, (char*)dyldCache
+images
[i
].pathFileOffset
);
667 printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra
[i
].exportsTrieAddr
, imagesExtra
[i
].exportsTrieSize
);
668 if ( imagesExtra
[i
].weakBindingsSize
)
669 printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra
[i
].weakBindingsAddr
, imagesExtra
[i
].weakBindingsSize
);
670 printf(" dependents: ");
671 for (uint32_t d
=imagesExtra
[i
].dependentsStartArrayIndex
; dependencyArray
[d
] != 0xFFFF; ++d
) {
672 uint16_t depIndex
= dependencyArray
[d
];
673 if ( depIndex
& 0x8000 )
674 printf(" up(%d) ", depIndex
& 0x7FFF);
676 printf(" %d ", depIndex
);
679 printf(" re-exports: ");
680 for (uint32_t r
=imagesExtra
[i
].reExportsStartArrayIndex
; reExportArray
[r
] != 0xFFFF; ++r
)
681 printf(" %d ", reExportArray
[r
]);
684 printf("libdyld.dylib:\n");
685 printf(" __dyld section address: 0x%llX\n", accelInfo
->dyldSectionAddr
);
686 printf("initializers (count=%u):\n", accelInfo
->initializersCount
);
687 const dyld_cache_accelerator_initializer
* initializers
= (dyld_cache_accelerator_initializer
*)((char*)accelInfo
+ accelInfo
->initializersOffset
);
688 for (uint32_t i
=0; i
< accelInfo
->initializersCount
; ++i
) {
689 printf(" image[%3u] 0x%llX\n", initializers
[i
].imageIndex
, mappings
[0].address
+ initializers
[i
].functionOffset
);
691 printf("DOF sections (count=%u):\n", accelInfo
->dofSectionsCount
);
692 const dyld_cache_accelerator_dof
* dofs
= (dyld_cache_accelerator_dof
*)((char*)accelInfo
+ accelInfo
->dofSectionsOffset
);
693 for (uint32_t i
=0; i
< accelInfo
->dofSectionsCount
; ++i
) {
694 printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs
[i
].imageIndex
, dofs
[i
].sectionAddress
, dofs
[i
].sectionAddress
+dofs
[i
].sectionSize
);
696 printf("bottom up order (count=%u):\n", accelInfo
->imageExtrasCount
);
697 const uint16_t* bottomUpArray
= (uint16_t*)((char*)accelInfo
+ accelInfo
->bottomUpListOffset
);
698 for (uint32_t i
=0; i
< accelInfo
->imageExtrasCount
; ++i
) {
699 unsigned imageIndex
= bottomUpArray
[i
];
700 if ( imageIndex
< accelInfo
->imageExtrasCount
)
701 printf(" image[%3u] %s\n", imageIndex
, (char*)dyldCache
+ images
[imageIndex
].pathFileOffset
);
703 printf(" image[%3u] BAD INDEX\n", imageIndex
);
705 printf("range table (count=%u):\n", accelInfo
->rangeTableCount
);
706 const dyld_cache_range_entry
* rangeTable
= (dyld_cache_range_entry
*)((char*)accelInfo
+ accelInfo
->rangeTableOffset
);
707 for (uint32_t i
=0; i
< accelInfo
->rangeTableCount
; ++i
) {
708 const dyld_cache_range_entry
& entry
= rangeTable
[i
];
709 printf(" 0x%llX -> 0x%llX %s\n", entry
.startAddress
, entry
.startAddress
+ entry
.size
, (char*)dyldCache
+ images
[entry
.imageIndex
].pathFileOffset
);
711 printf("dylib trie (size=%u):\n", accelInfo
->dylibTrieSize
);
712 const uint8_t* dylibTrieStart
= (uint8_t*)accelInfo
+ accelInfo
->dylibTrieOffset
;
713 const uint8_t* dylibTrieEnd
= dylibTrieStart
+ accelInfo
->dylibTrieSize
;
714 std::vector
<DylibIndexTrie::Entry
> dylibEntries
;
715 if ( !Trie
<DylibIndex
>::parseTrie(dylibTrieStart
, dylibTrieEnd
, dylibEntries
) )
716 printf(" malformed dylibs trie\n");
717 for (const DylibIndexTrie::Entry
& x
: dylibEntries
) {
718 printf(" image[%3u] %s\n", x
.info
.index
, x
.name
.c_str());
723 else if ( options
.mode
== modeTextInfo
) {
724 const dyld_cache_header
* header
= &dyldCache
->header
;
725 if ( (header
->mappingOffset
< sizeof(dyld_cache_header
)) || (header
->imagesTextCount
== 0) ) {
726 printf("no text info\n");
729 const dyld_cache_image_text_info
* imagesText
= (dyld_cache_image_text_info
*)((char*)dyldCache
+ header
->imagesTextOffset
);
730 const dyld_cache_image_text_info
* imagesTextEnd
= &imagesText
[header
->imagesTextCount
];
731 printf("dylib text infos (count=%llu):\n", header
->imagesTextCount
);
732 for (const dyld_cache_image_text_info
* p
=imagesText
; p
< imagesTextEnd
; ++p
) {
733 printf(" 0x%09llX -> 0x%09llX <", p
->loadAddress
, p
->loadAddress
+ p
->textSegmentSize
);
734 for (int i
=0; i
<16; ++i
) {
743 printf("%02X", p
->uuid
[i
]);
745 printf("> %s\n", (char*)dyldCache
+ p
->pathOffset
);
749 else if ( options
.mode
== modeLocalSymbols
) {
750 const dyld_cache_header
* header
= &dyldCache
->header
;
751 if ( header
->localSymbolsOffset
== 0 ) {
752 fprintf(stderr
, "Error: dyld shared cache does not contain local symbols info\n");
755 const bool is64
= (strstr((char*)dyldCache
, "64") != NULL
);
756 const dyld_cache_image_info
* imageInfos
= (dyld_cache_image_info
*)((char*)dyldCache
+ header
->imagesOffset
);
757 const dyld_cache_local_symbols_info
* localsInfo
= (dyld_cache_local_symbols_info
*)((char*)dyldCache
+ header
->localSymbolsOffset
);
758 const uint32_t nlistFileOffset
= (uint32_t)(header
->localSymbolsOffset
+ localsInfo
->nlistOffset
);
759 const uint32_t nlistCount
= localsInfo
->nlistCount
;
760 const uint32_t nlistByteSize
= is64
? nlistCount
*16 : nlistCount
*12;
761 const uint32_t stringsFileOffset
= (uint32_t)(header
->localSymbolsOffset
+ localsInfo
->stringsOffset
);
762 const uint32_t stringsSize
= localsInfo
->stringsSize
;
763 const uint32_t entriesCount
= localsInfo
->entriesCount
;
764 const dyld_cache_local_symbols_entry
* entries
= (dyld_cache_local_symbols_entry
*)((char*)localsInfo
+ localsInfo
->entriesOffset
);
765 printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize
/(1024*1024), nlistFileOffset
, nlistFileOffset
+nlistByteSize
);
766 printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize
/(1024*1024), stringsFileOffset
, stringsFileOffset
+stringsSize
);
767 printf("local symbols by dylib (count=%d):\n", entriesCount
);
768 //const char* stringPool = (char*)dyldCache + stringsFileOffset;
769 for (int i
=0; i
< entriesCount
; ++i
) {
770 const char* imageName
= (char*)dyldCache
+ imageInfos
[i
].pathFileOffset
;
771 printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries
[i
].nlistStartIndex
, entries
[i
].nlistCount
, imageName
);
774 const nlist_64
* symTab
= (nlist_64
*)((char*)dyldCache
+ nlistFileOffset
);
775 for (int e
=0; e
< entries
[i
].nlistCount(); ++e
) {
776 const nlist_64
* entry
= &symTab
[entries
[i
].nlistStartIndex()+e
];
777 printf(" nlist[%d].str=%d, %s\n", e
, entry
->n_un
.n_strx
, &stringPool
[entry
->n_un
.n_strx
]);
778 printf(" nlist[%d].value=0x%0llX\n", e
, entry
->n_value
);
784 else if ( options
.mode
== modeJSONMap
) {
785 std::string buffer
= dyldCache
->generateJSONMap("unknown");
786 printf("%s\n", buffer
.c_str());
788 else if ( options
.mode
== modeJSONDependents
) {
789 std::cout
<< dyldCache
->generateJSONDependents();
791 else if ( options
.mode
== modeStrings
) {
793 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
794 const dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
795 int64_t slide
= ma
->getSlide();
796 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
797 if ( ( (info
.sectFlags
& SECTION_TYPE
) == S_CSTRING_LITERALS
) ) {
798 if ( malformedSectionRange
) {
802 const uint8_t* content
= (uint8_t*)(info
.sectAddr
+ slide
);
803 const char* s
= (char*)content
;
804 const char* end
= s
+ info
.sectSize
;
806 printf("%s: %s\n", ma
->installName(), s
);
817 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
818 const dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
819 uint32_t exportTrieRuntimeOffset
;
820 uint32_t exportTrieSize
;
821 if ( ma
->hasExportTrie(exportTrieRuntimeOffset
, exportTrieSize
) ) {
822 const uint8_t* start
= (uint8_t*)mh
+ exportTrieRuntimeOffset
;
823 const uint8_t* end
= start
+ exportTrieSize
;
824 std::vector
<ExportInfoTrie::Entry
> exports
;
825 if ( !ExportInfoTrie::parseTrie(start
, end
, exports
) ) {
829 for (const ExportInfoTrie::Entry
& entry
: exports
) {
830 printf("%s: %s\n", ma
->installName(), entry
.name
.c_str());
836 else if ( options
.mode
== modeSectionSizes
) {
837 __block
std::map
<std::string
, uint64_t> sectionSizes
;
838 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
839 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
840 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
841 std::string section
= std::string(sectInfo
.segInfo
.segName
) + " " + sectInfo
.sectName
;
842 sectionSizes
[section
] += sectInfo
.sectSize
;
845 for (const auto& keyAndValue
: sectionSizes
) {
846 printf("%lld %s\n", keyAndValue
.second
, keyAndValue
.first
.c_str());
849 else if ( options
.mode
== modeObjCProtocols
) {
850 if ( dyldCache
->objcOpt() == nullptr ) {
851 fprintf(stderr
, "Error: could not get optimized objc\n");
854 objc_opt::objc_protocolopt2_t
* protocols
= dyldCache
->objcOpt()->protocolopt2();
855 if ( protocols
== nullptr ) {
856 fprintf(stderr
, "Error: could not get optimized objc protocols\n");
860 for (uint64_t index
= 0; index
!= protocols
->capacity
; ++index
) {
861 const objc_opt::objc_classheader_t
& clshi
= protocols
->classOffsets()[index
];
862 if ( clshi
.clsOffset
== 0 ) {
863 fprintf(stderr
, "[% 5lld]\n", index
);
866 const char* name
= (const char*)(((const uint8_t*)protocols
) + protocols
->offsets()[index
]);
867 if ( !clshi
.isDuplicate() ) {
868 fprintf(stderr
, "[% 5lld] -> (% 8d, % 8d) = %s\n", index
, clshi
.clsOffset
, clshi
.hiOffset
, name
);
872 // class appears in more than one header
873 uint32_t count
= clshi
.duplicateCount();
874 fprintf(stderr
, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n",
875 index
, clshi
.duplicateIndex(), clshi
.duplicateIndex() + clshi
.duplicateCount() - 1, name
);
877 const objc_opt::objc_classheader_t
*list
= &protocols
->duplicateOffsets()[clshi
.duplicateIndex()];
878 for (uint32_t i
= 0; i
< count
; i
++) {
879 fprintf(stderr
, " - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi
.duplicateIndex() + i
), list
[i
].clsOffset
, list
[i
].hiOffset
);
883 else if ( options
.mode
== modeObjCClasses
) {
884 using dyld3::json::Node
;
885 using ObjCClassInfo
= dyld3::MachOAnalyzer::ObjCClassInfo
;
886 const bool rebased
= false;
888 // Build a map of class vm addrs to their names so that categories know the
889 // name of the class they are attaching to
890 __block
std::map
<uint64_t, const char*> classVMAddrToName
;
891 __block
std::map
<uint64_t, const char*> metaclassVMAddrToName
;
892 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
893 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
894 const uint32_t pointerSize
= ma
->pointerSize();
896 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
897 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
898 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
899 dyld3::MachOAnalyzer::PrintableStringResult classNameResult
;
900 const char* className
= ma
->getPrintableString(objcClass
.nameVMAddr(pointerSize
), classNameResult
);
901 if (classNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
905 metaclassVMAddrToName
[classVMAddr
] = className
;
907 classVMAddrToName
[classVMAddr
] = className
;
911 ma
->forEachObjCClass(diag
, rebased
, visitClass
);
914 // These are used only for the on-disk binaries we analyze
915 __block
std::vector
<const char*> onDiskChainedFixupBindTargets
;
916 __block
std::map
<uint64_t, const char*> onDiskClassVMAddrToName
;
917 __block
std::map
<uint64_t, const char*> onDiskMetaclassVMAddrToName
;
920 auto makeNode
= [](std::string str
) -> Node
{
926 auto getProperties
= ^(const dyld3::MachOAnalyzer
* ma
, uint64_t propertiesVMAddr
) {
927 __block Node propertiesNode
;
928 auto visitProperty
= ^(uint64_t propertyVMAddr
, const dyld3::MachOAnalyzer::ObjCProperty
& property
) {
930 dyld3::MachOAnalyzer::PrintableStringResult propertyNameResult
;
931 const char* propertyName
= ma
->getPrintableString(property
.nameVMAddr
, propertyNameResult
);
932 if (propertyNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
935 // Get the attributes
936 dyld3::MachOAnalyzer::PrintableStringResult propertyAttributesResult
;
937 const char* propertyAttributes
= ma
->getPrintableString(property
.attributesVMAddr
, propertyAttributesResult
);
938 if (propertyAttributesResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
942 propertyNode
.map
["name"] = makeNode(propertyName
);
943 propertyNode
.map
["attributes"] = makeNode(propertyAttributes
);
944 propertiesNode
.array
.push_back(propertyNode
);
946 ma
->forEachObjCProperty(propertiesVMAddr
, rebased
, visitProperty
);
947 return propertiesNode
.array
.empty() ? std::optional
<Node
>() : propertiesNode
;
950 auto getClasses
= ^(const dyld3::MachOAnalyzer
* ma
) {
952 const uint32_t pointerSize
= ma
->pointerSize();
954 __block Node classesNode
;
955 __block
bool skippedPreviousClass
= false;
956 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
957 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
958 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
960 if (skippedPreviousClass
) {
961 // If the class was bad, then skip the meta class too
962 skippedPreviousClass
= false;
966 skippedPreviousClass
= true;
969 std::string classType
= "-";
972 dyld3::MachOAnalyzer::PrintableStringResult classNameResult
;
973 const char* className
= ma
->getPrintableString(objcClass
.nameVMAddr(pointerSize
), classNameResult
);
974 if (classNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
) {
978 const char* superClassName
= nullptr;
979 if ( ma
->inDyldCache() ) {
980 if ( objcClass
.superclassVMAddr
!= 0 ) {
982 // If we are root class, then our superclass should actually point to our own class
983 const uint32_t RO_ROOT
= (1<<1);
984 if ( objcClass
.flags(pointerSize
) & RO_ROOT
) {
985 auto it
= classVMAddrToName
.find(objcClass
.superclassVMAddr
);
986 assert(it
!= classVMAddrToName
.end());
987 superClassName
= it
->second
;
989 auto it
= metaclassVMAddrToName
.find(objcClass
.superclassVMAddr
);
990 assert(it
!= metaclassVMAddrToName
.end());
991 superClassName
= it
->second
;
994 auto it
= classVMAddrToName
.find(objcClass
.superclassVMAddr
);
995 assert(it
!= classVMAddrToName
.end());
996 superClassName
= it
->second
;
1000 // On-disk binary. Lets crack the chain to work out what we are pointing at
1001 dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup
;
1002 fixup
.raw64
= objcClass
.superclassVMAddr
;
1003 assert(!fixup
.arm64e
.authBind
.auth
);
1004 if (fixup
.arm64e
.bind
.bind
) {
1005 // Bind to another image. Use the bind table to work out which name to bind to
1006 const char* symbolName
= onDiskChainedFixupBindTargets
[fixup
.arm64e
.bind
.ordinal
];
1008 if ( strstr(symbolName
, "_OBJC_METACLASS_$_") == symbolName
) {
1009 superClassName
= symbolName
+ strlen("_OBJC_METACLASS_$_");
1011 // Swift classes don't start with these prefixes so just skip them
1012 if (objcClass
.isSwiftLegacy
|| objcClass
.isSwiftStable
)
1016 if ( strstr(symbolName
, "_OBJC_CLASS_$_") == symbolName
) {
1017 superClassName
= symbolName
+ strlen("_OBJC_CLASS_$_");
1019 // Swift classes don't start with these prefixes so just skip them
1020 if (objcClass
.isSwiftLegacy
|| objcClass
.isSwiftStable
)
1025 // Rebase within this image.
1027 auto it
= onDiskMetaclassVMAddrToName
.find(objcClass
.superclassVMAddr
);
1028 assert(it
!= onDiskMetaclassVMAddrToName
.end());
1029 superClassName
= it
->second
;
1031 auto it
= onDiskClassVMAddrToName
.find(objcClass
.superclassVMAddr
);
1032 assert(it
!= onDiskClassVMAddrToName
.end());
1033 superClassName
= it
->second
;
1038 // Print the methods on this class
1039 __block Node methodsNode
;
1040 auto visitMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1041 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult
;
1042 const char* methodName
= ma
->getPrintableString(method
.nameVMAddr
, methodNameResult
);
1043 if (methodNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
1045 methodsNode
.array
.push_back(makeNode(classType
+ methodName
));
1047 ma
->forEachObjCMethod(objcClass
.baseMethodsVMAddr(pointerSize
), rebased
,
1050 std::optional
<Node
> properties
= getProperties(ma
, objcClass
.basePropertiesVMAddr(pointerSize
));
1053 assert(!classesNode
.array
.empty());
1054 Node
& currentClassNode
= classesNode
.array
.back();
1055 assert(currentClassNode
.map
["className"].value
== className
);
1056 if (!methodsNode
.array
.empty()) {
1057 Node
& currentMethodsNode
= currentClassNode
.map
["methods"];
1058 currentMethodsNode
.array
.insert(currentMethodsNode
.array
.end(),
1059 methodsNode
.array
.begin(),
1060 methodsNode
.array
.end());
1062 if (properties
.has_value()) {
1063 Node
& currentPropertiesNode
= currentClassNode
.map
["properties"];
1064 currentPropertiesNode
.array
.insert(currentPropertiesNode
.array
.end(),
1065 properties
->array
.begin(),
1066 properties
->array
.end());
1071 Node currentClassNode
;
1072 currentClassNode
.map
["className"] = makeNode(className
);
1073 if ( superClassName
!= nullptr )
1074 currentClassNode
.map
["superClassName"] = makeNode(superClassName
);
1075 if (!methodsNode
.array
.empty())
1076 currentClassNode
.map
["methods"] = methodsNode
;
1077 if (properties
.has_value())
1078 currentClassNode
.map
["properties"] = properties
.value();
1080 // We didn't skip this class so mark it as such
1081 skippedPreviousClass
= false;
1083 classesNode
.array
.push_back(currentClassNode
);
1086 ma
->forEachObjCClass(diag
, rebased
, visitClass
);
1087 return classesNode
.array
.empty() ? std::optional
<Node
>() : classesNode
;
1090 auto getCategories
= ^(const dyld3::MachOAnalyzer
* ma
) {
1093 __block Node categoriesNode
;
1094 auto visitCategory
= ^(Diagnostics
& diag
, uint64_t categoryVMAddr
,
1095 const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) {
1096 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult
;
1097 const char* categoryName
= ma
->getPrintableString(objcCategory
.nameVMAddr
, categoryNameResult
);
1098 if (categoryNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
1101 const char* className
= nullptr;
1102 if ( ma
->inDyldCache() ) {
1103 auto it
= classVMAddrToName
.find(objcCategory
.clsVMAddr
);
1104 assert(it
!= classVMAddrToName
.end());
1105 className
= it
->second
;
1107 // On-disk binary. Lets crack the chain to work out what we are pointing at
1108 dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup
;
1109 fixup
.raw64
= objcCategory
.clsVMAddr
;
1110 assert(!fixup
.arm64e
.authBind
.auth
);
1111 if (fixup
.arm64e
.bind
.bind
) {
1112 // Bind to another image. Use the bind table to work out which name to bind to
1113 const char* symbolName
= onDiskChainedFixupBindTargets
[fixup
.arm64e
.bind
.ordinal
];
1114 if ( strstr(symbolName
, "_OBJC_CLASS_$_") == symbolName
) {
1115 className
= symbolName
+ strlen("_OBJC_CLASS_$_");
1117 // Swift classes don't start with these prefixes so just skip them
1118 // We don't know that this is a Swift class/category though, but skip it anyway
1122 auto it
= onDiskClassVMAddrToName
.find(objcCategory
.clsVMAddr
);
1123 if (it
== onDiskClassVMAddrToName
.end()) {
1124 // This is an odd binary with perhaps a Swift class. Just skip this entry
1127 className
= it
->second
;
1131 // Print the instance methods on this category
1132 __block Node methodsNode
;
1133 auto visitInstanceMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1134 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult
;
1135 const char* methodName
= ma
->getPrintableString(method
.nameVMAddr
, methodNameResult
);
1136 if (methodNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
1138 methodsNode
.array
.push_back(makeNode(std::string("-") + methodName
));
1140 ma
->forEachObjCMethod(objcCategory
.instanceMethodsVMAddr
, rebased
,
1141 visitInstanceMethod
);
1143 // Print the instance methods on this category
1144 __block Node classMethodsNode
;
1145 auto visitClassMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1146 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult
;
1147 const char* methodName
= ma
->getPrintableString(method
.nameVMAddr
, methodNameResult
);
1148 if (methodNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
1150 methodsNode
.array
.push_back(makeNode(std::string("+") + methodName
));
1152 ma
->forEachObjCMethod(objcCategory
.classMethodsVMAddr
, rebased
,
1155 Node currentCategoryNode
;
1156 currentCategoryNode
.map
["categoryName"] = makeNode(categoryName
);
1157 currentCategoryNode
.map
["className"] = makeNode(className
);
1158 if (!methodsNode
.array
.empty())
1159 currentCategoryNode
.map
["methods"] = methodsNode
;
1160 if (std::optional
<Node
> properties
= getProperties(ma
, objcCategory
.instancePropertiesVMAddr
))
1161 currentCategoryNode
.map
["properties"] = properties
.value();
1163 categoriesNode
.array
.push_back(currentCategoryNode
);
1166 ma
->forEachObjCCategory(diag
, rebased
, visitCategory
);
1167 return categoriesNode
.array
.empty() ? std::optional
<Node
>() : categoriesNode
;
1170 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
1171 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
1174 imageRecord
.map
["imagePath"] = makeNode(installName
);
1175 imageRecord
.map
["imageType"] = makeNode("cache-dylib");
1176 std::optional
<Node
> classes
= getClasses(ma
);
1177 std::optional
<Node
> categories
= getCategories(ma
);
1179 // Skip emitting images with no objc data
1180 if (!classes
.has_value() && !categories
.has_value())
1182 if (classes
.has_value())
1183 imageRecord
.map
["classes"] = classes
.value();
1184 if (categories
.has_value())
1185 imageRecord
.map
["categories"] = categories
.value();
1187 root
.array
.push_back(imageRecord
);
1190 FileSystemPhysical fileSystem
;
1191 dyld3::Platform platform
= dyldCache
->platform();
1192 const dyld3::GradedArchs
& archs
= dyld3::GradedArchs::forName(dyldCache
->archName(), true);
1194 dyldCache
->forEachLaunchClosure(^(const char *executableRuntimePath
, const dyld3::closure::LaunchClosure
*closure
) {
1196 char realerPath
[MAXPATHLEN
];
1197 dyld3::closure::LoadedFileInfo loadedFileInfo
= dyld3::MachOAnalyzer::load(diag
, fileSystem
, executableRuntimePath
, archs
, platform
, realerPath
);
1198 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)loadedFileInfo
.fileContent
;
1199 uint32_t pointerSize
= ma
->pointerSize();
1201 // Populate the bind targets for classes from other images
1202 onDiskChainedFixupBindTargets
.clear();
1203 ma
->forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
1204 onDiskChainedFixupBindTargets
.push_back(symbolName
);
1206 if ( diag
.hasError() )
1209 // Populate the rebase targets for class names
1210 onDiskMetaclassVMAddrToName
.clear();
1211 onDiskClassVMAddrToName
.clear();
1212 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
1213 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
1214 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
1215 dyld3::MachOAnalyzer::PrintableStringResult classNameResult
;
1216 const char* className
= ma
->getPrintableString(objcClass
.nameVMAddr(pointerSize
), classNameResult
);
1217 if (classNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
1221 onDiskMetaclassVMAddrToName
[classVMAddr
] = className
;
1223 onDiskClassVMAddrToName
[classVMAddr
] = className
;
1226 ma
->forEachObjCClass(diag
, rebased
, visitClass
);
1229 imageRecord
.map
["imagePath"] = makeNode(executableRuntimePath
);
1230 imageRecord
.map
["imageType"] = makeNode("executable");
1231 std::optional
<Node
> classes
= getClasses(ma
);
1232 std::optional
<Node
> categories
= getCategories(ma
);
1234 // Skip emitting images with no objc data
1235 if (!classes
.has_value() && !categories
.has_value())
1237 if (classes
.has_value())
1238 imageRecord
.map
["classes"] = classes
.value();
1239 if (categories
.has_value())
1240 imageRecord
.map
["categories"] = categories
.value();
1242 root
.array
.push_back(imageRecord
);
1245 dyldCache
->forEachDlopenImage(^(const char *runtimePath
, const dyld3::closure::Image
*image
) {
1247 char realerPath
[MAXPATHLEN
];
1248 dyld3::closure::LoadedFileInfo loadedFileInfo
= dyld3::MachOAnalyzer::load(diag
, fileSystem
, runtimePath
, archs
, platform
, realerPath
);
1249 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)loadedFileInfo
.fileContent
;
1250 uint32_t pointerSize
= ma
->pointerSize();
1252 // Populate the bind targets for classes from other images
1253 onDiskChainedFixupBindTargets
.clear();
1254 ma
->forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
1255 onDiskChainedFixupBindTargets
.push_back(symbolName
);
1257 if ( diag
.hasError() )
1260 // Populate the rebase targets for class names
1261 onDiskMetaclassVMAddrToName
.clear();
1262 onDiskClassVMAddrToName
.clear();
1263 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
1264 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
1265 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
1266 dyld3::MachOAnalyzer::PrintableStringResult classNameResult
;
1267 const char* className
= ma
->getPrintableString(objcClass
.nameVMAddr(pointerSize
), classNameResult
);
1268 if (classNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
1272 onDiskMetaclassVMAddrToName
[classVMAddr
] = className
;
1274 onDiskClassVMAddrToName
[classVMAddr
] = className
;
1277 ma
->forEachObjCClass(diag
, rebased
, visitClass
);
1280 imageRecord
.map
["imagePath"] = makeNode(runtimePath
);
1281 imageRecord
.map
["imageType"] = makeNode("non-cache-dylib");
1282 std::optional
<Node
> classes
= getClasses(ma
);
1283 std::optional
<Node
> categories
= getCategories(ma
);
1285 // Skip emitting images with no objc data
1286 if (!classes
.has_value() && !categories
.has_value())
1288 if (classes
.has_value())
1289 imageRecord
.map
["classes"] = classes
.value();
1290 if (categories
.has_value())
1291 imageRecord
.map
["categories"] = categories
.value();
1293 root
.array
.push_back(imageRecord
);
1296 dyld3::json::printJSON(root
, 0, std::cout
);
1298 else if ( options
.mode
== modeObjCSelectors
) {
1299 if ( dyldCache
->objcOpt() == nullptr ) {
1300 fprintf(stderr
, "Error: could not get optimized objc\n");
1303 const objc_opt::objc_selopt_t
* selectors
= dyldCache
->objcOpt()->selopt();
1304 if ( selectors
== nullptr ) {
1305 fprintf(stderr
, "Error: could not get optimized objc selectors\n");
1309 std::vector
<const char*> selNames
;
1310 for (uint64_t index
= 0; index
!= selectors
->capacity
; ++index
) {
1311 objc_opt::objc_stringhash_offset_t offset
= selectors
->offsets()[index
];
1314 const char* selName
= selectors
->getEntryForIndex((uint32_t)index
);
1315 selNames
.push_back(selName
);
1318 std::sort(selNames
.begin(), selNames
.end(),
1319 [](const char* a
, const char* b
) {
1320 // Sort by offset, not string value
1324 auto makeNode
= [](std::string str
) {
1325 dyld3::json::Node node
;
1330 dyld3::json::Node root
;
1331 for (const char* selName
: selNames
) {
1332 dyld3::json::Node selNode
;
1333 selNode
.map
["selectorName"] = makeNode(selName
);
1334 selNode
.map
["offset"] = makeNode(dyld3::json::decimal((uint64_t)selName
- (uint64_t)dyldCache
));
1336 root
.array
.push_back(selNode
);
1339 dyld3::json::printJSON(root
, 0, std::cout
);
1341 else if ( options
.mode
== modeExtract
) {
1342 char pathBuffer
[PATH_MAX
];
1343 uint32_t bufferSize
= PATH_MAX
;
1344 if ( _NSGetExecutablePath(pathBuffer
, &bufferSize
) != 0 ) {
1345 fprintf(stderr
, "Error: could not get path of program\n");
1348 char* last
= strrchr(pathBuffer
, '/');
1349 // The bundle is at a different location on device. Its /usr/lib/dsc_extractor.bundle in the SDK
1350 // but /usr/local/lib/dsc_extractor.bundle on device.
1351 strcpy(last
+1, DSC_BUNDLE_REL_PATH
);
1352 void* handle
= dlopen(pathBuffer
, RTLD_LAZY
);
1353 if ( handle
== NULL
) {
1354 fprintf(stderr
, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer
);
1358 typedef int (*extractor_proc
)(const char* shared_cache_file_path
, const char* extraction_root_path
,
1359 void (^progress
)(unsigned current
, unsigned total
));
1361 extractor_proc proc
= (extractor_proc
)dlsym(handle
, "dyld_shared_cache_extract_dylibs_progress");
1362 if ( proc
== NULL
) {
1363 fprintf(stderr
, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
1367 int result
= (*proc
)(sharedCachePath
, options
.extractionDir
, ^(unsigned c
, unsigned total
) { } );
1371 switch ( options
.mode
) {
1373 if (options
.printInodes
) {
1374 dyldCache
->forEachImageEntry(^(const char* path
, uint64_t mTime
, uint64_t inode
) {
1375 printf("0x%08llX 0x%08llX ", inode
, mTime
);
1376 if ( isAlias(path
, dyldCache
) )
1377 printf("[alias] %s\n", path
);
1379 printf("%s\n", path
);
1382 dyldCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const unsigned char *dylibUUID
, const char *installName
, bool &stop
) {
1383 if ( options
.printVMAddrs
)
1384 printf("0x%08llX ", loadAddressUnslid
);
1385 if ( options
.printUUIDs
) {
1386 const uint8_t* uuid
= (uint8_t*)dylibUUID
;
1387 printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ",
1388 uuid
[0], uuid
[1], uuid
[2], uuid
[3],
1389 uuid
[4], uuid
[5], uuid
[6], uuid
[7],
1390 uuid
[8], uuid
[9], uuid
[10], uuid
[11],
1391 uuid
[12], uuid
[13], uuid
[14], uuid
[15]);
1393 if ( isAlias(installName
, dyldCache
) )
1394 printf("[alias] %s\n", installName
);
1396 printf("%s\n", installName
);
1402 __block
std::map
<uint64_t, const char*> dataSegNames
;
1403 __block
std::map
<uint64_t, uint64_t> dataSegEnds
;
1404 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1405 dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)mh
;
1406 mf
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo
&info
, bool &stop
) {
1407 if ( isAlias(installName
, dyldCache
) )
1409 printf("0x%08llX - 0x%08llX %s %s\n", info
.vmAddr
, info
.vmAddr
+ info
.vmSize
, info
.segName
, installName
);
1410 if ( strncmp(info
.segName
, "__DATA", 6) == 0 ) {
1411 dataSegNames
[info
.vmAddr
] = installName
;
1412 dataSegEnds
[info
.vmAddr
] = info
.vmAddr
+ info
.vmSize
;
1416 // <rdar://problem/51084507> Enhance dyld_shared_cache_util to show where section alignment added padding
1417 uint64_t lastEnd
= 0;
1418 for (const auto& entry
: dataSegEnds
) {
1419 uint64_t padding
= entry
.first
- lastEnd
;
1420 if ( (padding
> 32) && (lastEnd
!= 0) ) {
1421 printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd
, entry
.first
, padding
/1024);
1423 lastEnd
= entry
.second
;
1427 case modeDependencies
: {
1428 __block
bool dependentTargetFound
= false;
1429 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1430 if ( strcmp(options
.dependentsOfPath
, installName
) != 0 )
1432 dependentTargetFound
= true;
1434 auto printDep
= [&options
](const char *loadPath
, uint32_t compatVersion
, uint32_t curVersion
) {
1435 if ( options
.printDylibVersions
) {
1436 uint32_t compat_vers
= compatVersion
;
1437 uint32_t current_vers
= curVersion
;
1438 printf("\t%s", loadPath
);
1439 if ( compat_vers
!= 0xFFFFFFFF ) {
1440 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
1441 (compat_vers
>> 16),
1442 (compat_vers
>> 8) & 0xff,
1443 (compat_vers
) & 0xff,
1444 (current_vers
>> 16),
1445 (current_vers
>> 8) & 0xff,
1446 (current_vers
) & 0xff);
1453 printf("\t%s\n", loadPath
);
1457 dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)mh
;
1459 // First print out our dylib and version.
1460 const char* dylibInstallName
;
1461 uint32_t currentVersion
;
1462 uint32_t compatVersion
;
1463 if ( mf
->getDylibInstallName(&dylibInstallName
, &compatVersion
, ¤tVersion
) ) {
1464 printDep(dylibInstallName
, compatVersion
, currentVersion
);
1467 // Then the dependent dylibs.
1468 mf
->forEachDependentDylib(^(const char *loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
1469 printDep(loadPath
, compatVersion
, curVersion
);
1472 if (options
.dependentsOfPath
&& !dependentTargetFound
) {
1473 fprintf(stderr
, "Error: could not find '%s' in the shared cache at\n %s\n", options
.dependentsOfPath
, sharedCachePath
);
1478 case modeLinkEdit
: {
1479 std::map
<uint32_t, const char*> pageToContent
;
1480 auto add_linkedit
= [&pageToContent
](uint32_t pageStart
, uint32_t pageEnd
, const char* message
) {
1481 for (uint32_t p
= pageStart
; p
<= pageEnd
; p
+= 4096) {
1482 std::map
<uint32_t, const char*>::iterator pos
= pageToContent
.find(p
);
1483 if ( pos
== pageToContent
.end() ) {
1484 pageToContent
[p
] = strdup(message
);
1487 const char* oldMessage
= pos
->second
;
1489 asprintf(&newMesssage
, "%s, %s", oldMessage
, message
);
1490 pageToContent
[p
] = newMesssage
;
1491 ::free((void*)oldMessage
);
1496 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1497 // Filter out symlinks.
1498 if (isAlias(installName
, dyldCache
))
1500 dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
1502 dyld3::MachOAnalyzer::LinkEditInfo leInfo
;
1503 ma
->getLinkEditPointers(diag
, leInfo
);
1505 if (diag
.hasError())
1509 const char* shortName
= strrchr(installName
, '/') + 1;
1510 // add export trie info
1511 if ( leInfo
.dyldInfo
->export_size
!= 0 ) {
1512 //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off());
1513 uint32_t exportPageOffsetStart
= leInfo
.dyldInfo
->export_off
& (-4096);
1514 uint32_t exportPageOffsetEnd
= (leInfo
.dyldInfo
->export_off
+ leInfo
.dyldInfo
->export_size
) & (-4096);
1515 sprintf(message
, "exports from %s", shortName
);
1516 add_linkedit(exportPageOffsetStart
, exportPageOffsetEnd
, message
);
1519 if ( leInfo
.dyldInfo
->bind_size
!= 0 ) {
1520 uint32_t bindPageOffsetStart
= leInfo
.dyldInfo
->bind_off
& (-4096);
1521 uint32_t bindPageOffsetEnd
= (leInfo
.dyldInfo
->bind_off
+ leInfo
.dyldInfo
->bind_size
) & (-4096);
1522 sprintf(message
, "bindings from %s", shortName
);
1523 add_linkedit(bindPageOffsetStart
, bindPageOffsetEnd
, message
);
1525 // add lazy binding info
1526 if ( leInfo
.dyldInfo
->lazy_bind_size
!= 0 ) {
1527 uint32_t lazybindPageOffsetStart
= leInfo
.dyldInfo
->lazy_bind_off
& (-4096);
1528 uint32_t lazybindPageOffsetEnd
= (leInfo
.dyldInfo
->lazy_bind_off
+ leInfo
.dyldInfo
->lazy_bind_size
) & (-4096);
1529 sprintf(message
, "lazy bindings from %s", shortName
);
1530 add_linkedit(lazybindPageOffsetStart
, lazybindPageOffsetEnd
, message
);
1532 // add weak binding info
1533 if ( leInfo
.dyldInfo
->weak_bind_size
!= 0 ) {
1534 uint32_t weakbindPageOffsetStart
= leInfo
.dyldInfo
->weak_bind_off
& (-4096);
1535 uint32_t weakbindPageOffsetEnd
= (leInfo
.dyldInfo
->weak_bind_off
+ leInfo
.dyldInfo
->weak_bind_size
) & (-4096);
1536 sprintf(message
, "weak bindings from %s", shortName
);
1537 add_linkedit(weakbindPageOffsetStart
, weakbindPageOffsetEnd
, message
);
1541 for (std::map
<uint32_t, const char*>::iterator it
= pageToContent
.begin(); it
!= pageToContent
.end(); ++it
) {
1542 printf("0x%08X %s\n", it
->first
, it
->second
);
1551 __block
std::vector
<TextInfo
> textSegments
;
1552 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1553 // Filter out symlinks.
1554 if (isAlias(installName
, dyldCache
))
1557 dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
1558 ma
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo
&info
, bool &stop
) {
1559 if ( strcmp(info
.segName
, "__TEXT") != 0 )
1561 textSegments
.push_back({ info
.fileSize
, installName
});
1564 std::sort(textSegments
.begin(), textSegments
.end(), [](const TextInfo
& left
, const TextInfo
& right
) {
1565 return (left
.textSize
> right
.textSize
);
1567 for (std::vector
<TextInfo
>::iterator it
= textSegments
.begin(); it
!= textSegments
.end(); ++it
) {
1568 printf(" 0x%08llX %s\n", it
->textSize
, it
->path
);
1575 case modeVerboseSlideInfo
:
1576 case modeAcceleratorInfo
:
1578 case modeLocalSymbols
:
1580 case modeJSONDependents
:
1581 case modeSectionSizes
:
1583 case modeObjCProtocols
:
1584 case modeObjCClasses
:
1585 case modeObjCSelectors
: