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
= 32 - __builtin_clz(possibleSlideValues
- 1);
615 printf("ASLR entropy: %u-bits\n", entropyBits
);
617 printf("mappings:\n");
618 dyldCache
->forEachRegion(^(const void *content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
,
620 std::string mappingName
= "";
621 if ( permissions
& VM_PROT_EXECUTE
) {
622 mappingName
= "__TEXT";
623 } else if ( permissions
& VM_PROT_WRITE
) {
624 // Start off with __DATA or __AUTH
625 if ( flags
& DYLD_CACHE_MAPPING_AUTH_DATA
)
626 mappingName
= "__AUTH";
628 mappingName
= "__DATA";
629 // Then add one of "", _DIRTY, or _CONST
630 if ( flags
& DYLD_CACHE_MAPPING_DIRTY_DATA
)
631 mappingName
+= "_DIRTY";
632 else if ( flags
& DYLD_CACHE_MAPPING_CONST_DATA
)
633 mappingName
+= "_CONST";
635 else if ( permissions
& VM_PROT_READ
) {
636 mappingName
= "__LINKEDIT";
638 mappingName
= "*unknown*";
640 uint64_t fileOffset
= (uint8_t*)content
- (uint8_t*)dyldCache
;
641 printf("%16s %4lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
642 mappingName
.c_str(), size
/ (1024*1024), fileOffset
, fileOffset
+ size
, vmAddr
, vmAddr
+ size
);
644 if ( header
->codeSignatureOffset
!= 0 ) {
645 uint64_t size
= header
->codeSignatureSize
;
646 uint64_t csAddr
= dyldCache
->getCodeSignAddress();
648 printf("%16s %4lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
649 "code sign", size
/(1024*1024), header
->codeSignatureOffset
, header
->codeSignatureOffset
+ size
, csAddr
, csAddr
+ size
);
651 dyldCache
->forEachSlideInfo(^(uint64_t mappingStartAddress
, uint64_t mappingSize
, const uint8_t *mappingPagesStart
,
652 uint64_t slideInfoOffset
, uint64_t slideInfoSize
, const dyld_cache_slide_info
*slideInfoHeader
) {
654 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
655 slideInfoSize
/1024, slideInfoOffset
, slideInfoOffset
+ slideInfoSize
);
657 if ( header
->localSymbolsOffset
!= 0 )
658 printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
659 header
->localSymbolsSize
/(1024*1024), header
->localSymbolsOffset
, header
->localSymbolsOffset
+ header
->localSymbolsSize
);
661 else if ( options
.mode
== modeTextInfo
) {
662 const dyld_cache_header
* header
= &dyldCache
->header
;
663 printf("dylib text infos (count=%llu):\n", header
->imagesTextCount
);
664 dyldCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const unsigned char *dylibUUID
, const char *installName
, bool &stop
) {
665 uuid_string_t uuidString
;
666 uuid_unparse_upper(dylibUUID
, uuidString
);
667 printf(" 0x%09llX -> 0x%09llX <%s> %s\n", loadAddressUnslid
, loadAddressUnslid
+ textSegmentSize
, uuidString
, installName
);
670 else if ( options
.mode
== modeLocalSymbols
) {
671 if ( !dyldCache
->hasLocalSymbolsInfo() ) {
672 fprintf(stderr
, "Error: dyld shared cache does not contain local symbols info\n");
675 const bool is64
= (strstr(dyldCache
->archName(), "64") != NULL
);
676 const uint32_t nlistFileOffset
= (uint32_t)((uint8_t*)dyldCache
->getLocalNlistEntries() - (uint8_t*)dyldCache
);
677 const uint32_t nlistCount
= dyldCache
->getLocalNlistCount();
678 const uint32_t nlistByteSize
= is64
? nlistCount
*16 : nlistCount
*12;
679 const char* localStrings
= dyldCache
->getLocalStrings();
680 const uint32_t stringsFileOffset
= (uint32_t)((uint8_t*)localStrings
- (uint8_t*)dyldCache
);
681 const uint32_t stringsSize
= dyldCache
->getLocalStringsSize();
683 printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize
/(1024*1024), nlistFileOffset
, nlistFileOffset
+nlistByteSize
);
684 printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize
/(1024*1024), stringsFileOffset
, stringsFileOffset
+stringsSize
);
686 __block
uint32_t entriesCount
= 0;
687 dyldCache
->forEachLocalSymbolEntry(^(uint32_t dylibOffset
, uint32_t nlistStartIndex
, uint32_t nlistCount
, bool &stop
) {
688 const char* imageName
= dyldCache
->getIndexedImagePath(entriesCount
);
689 printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", nlistStartIndex
, nlistCount
, imageName
);
692 const nlist_64
* symTab
= (nlist_64
*)((char*)dyldCache
+ nlistFileOffset
);
693 for (int e
= 0; e
< nlistCount
; ++e
) {
694 const nlist_64
* entry
= &symTab
[nlistStartIndex
+ e
];
695 printf(" nlist[%d].str=%d, %s\n", e
, entry
->n_un
.n_strx
, &localStrings
[entry
->n_un
.n_strx
]);
696 printf(" nlist[%d].value=0x%0llX\n", e
, entry
->n_value
);
702 printf("local symbols by dylib (count=%d):\n", entriesCount
);
704 else if ( options
.mode
== modeJSONMap
) {
705 std::string buffer
= dyldCache
->generateJSONMap("unknown");
706 printf("%s\n", buffer
.c_str());
708 else if ( options
.mode
== modeJSONDependents
) {
709 std::cout
<< dyldCache
->generateJSONDependents();
711 else if ( options
.mode
== modeStrings
) {
713 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
714 const dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
715 int64_t slide
= ma
->getSlide();
716 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
717 if ( ( (info
.sectFlags
& SECTION_TYPE
) == S_CSTRING_LITERALS
) ) {
718 if ( malformedSectionRange
) {
722 const uint8_t* content
= (uint8_t*)(info
.sectAddr
+ slide
);
723 const char* s
= (char*)content
;
724 const char* end
= s
+ info
.sectSize
;
726 printf("%s: %s\n", ma
->installName(), s
);
737 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
738 const dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
739 uint32_t exportTrieRuntimeOffset
;
740 uint32_t exportTrieSize
;
741 if ( ma
->hasExportTrie(exportTrieRuntimeOffset
, exportTrieSize
) ) {
742 const uint8_t* start
= (uint8_t*)mh
+ exportTrieRuntimeOffset
;
743 const uint8_t* end
= start
+ exportTrieSize
;
744 std::vector
<ExportInfoTrie::Entry
> exports
;
745 if ( !ExportInfoTrie::parseTrie(start
, end
, exports
) ) {
749 for (const ExportInfoTrie::Entry
& entry
: exports
) {
750 printf("%s: %s\n", ma
->installName(), entry
.name
.c_str());
756 else if ( options
.mode
== modeSectionSizes
) {
757 __block
std::map
<std::string
, uint64_t> sectionSizes
;
758 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
759 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
760 ma
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stop
) {
761 std::string section
= std::string(sectInfo
.segInfo
.segName
) + " " + sectInfo
.sectName
;
762 sectionSizes
[section
] += sectInfo
.sectSize
;
765 for (const auto& keyAndValue
: sectionSizes
) {
766 printf("%lld %s\n", keyAndValue
.second
, keyAndValue
.first
.c_str());
769 else if ( options
.mode
== modeObjCProtocols
) {
770 if ( dyldCache
->objcOpt() == nullptr ) {
771 fprintf(stderr
, "Error: could not get optimized objc\n");
774 objc_opt::objc_protocolopt2_t
* protocols
= dyldCache
->objcOpt()->protocolopt2();
775 if ( protocols
== nullptr ) {
776 fprintf(stderr
, "Error: could not get optimized objc protocols\n");
780 for (uint64_t index
= 0; index
!= protocols
->capacity
; ++index
) {
781 const objc_opt::objc_classheader_t
& clshi
= protocols
->classOffsets()[index
];
782 if ( clshi
.clsOffset
== 0 ) {
783 fprintf(stderr
, "[% 5lld]\n", index
);
786 const char* name
= (const char*)(((const uint8_t*)protocols
) + protocols
->offsets()[index
]);
787 if ( !clshi
.isDuplicate() ) {
788 fprintf(stderr
, "[% 5lld] -> (% 8d, % 8d) = %s\n", index
, clshi
.clsOffset
, clshi
.hiOffset
, name
);
792 // class appears in more than one header
793 uint32_t count
= clshi
.duplicateCount();
794 fprintf(stderr
, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n",
795 index
, clshi
.duplicateIndex(), clshi
.duplicateIndex() + clshi
.duplicateCount() - 1, name
);
797 const objc_opt::objc_classheader_t
*list
= &protocols
->duplicateOffsets()[clshi
.duplicateIndex()];
798 for (uint32_t i
= 0; i
< count
; i
++) {
799 fprintf(stderr
, " - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi
.duplicateIndex() + i
), list
[i
].clsOffset
, list
[i
].hiOffset
);
803 else if ( options
.mode
== modeObjCClasses
) {
804 using dyld3::json::Node
;
805 using dyld3::json::NodeValueType
;
806 using ObjCClassInfo
= dyld3::MachOAnalyzer::ObjCClassInfo
;
807 const bool rebased
= false;
809 std::string
instancePrefix("-");
810 std::string
classPrefix("+");
812 auto getString
= ^const char *(const dyld3::MachOAnalyzer
* ma
, uint64_t nameVMAddr
){
813 dyld3::MachOAnalyzer::PrintableStringResult result
;
814 const char* name
= ma
->getPrintableString(nameVMAddr
, result
);
815 if (result
== dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
820 // Build a map of class vm addrs to their names so that categories know the
821 // name of the class they are attaching to
822 __block
std::unordered_map
<uint64_t, const char*> classVMAddrToName
;
823 __block
std::unordered_map
<uint64_t, const char*> metaclassVMAddrToName
;
824 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
825 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
826 const uint32_t pointerSize
= ma
->pointerSize();
828 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
829 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
830 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
831 if (auto className
= getString(ma
, objcClass
.nameVMAddr(pointerSize
))) {
833 metaclassVMAddrToName
[classVMAddr
] = className
;
835 classVMAddrToName
[classVMAddr
] = className
;
841 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= dyldCache
->makeVMAddrConverter(rebased
);
842 ma
->forEachObjCClass(diag
, vmAddrConverter
, visitClass
);
845 // These are used only for the on-disk binaries we analyze
846 __block
std::vector
<const char*> onDiskChainedFixupBindTargets
;
847 __block
std::unordered_map
<uint64_t, const char*> onDiskClassVMAddrToName
;
848 __block
std::unordered_map
<uint64_t, const char*> onDiskMetaclassVMAddrToName
;
850 auto getProperties
= ^(const dyld3::MachOAnalyzer
* ma
, uint64_t propertiesVMAddr
,
851 const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) {
852 __block Node propertiesNode
;
853 auto visitProperty
= ^(uint64_t propertyVMAddr
, const dyld3::MachOAnalyzer::ObjCProperty
& property
) {
854 // Get the name && attributes
855 auto propertyName
= getString(ma
, property
.nameVMAddr
);
856 auto propertyAttributes
= getString(ma
, property
.attributesVMAddr
);
858 if (!propertyName
|| !propertyAttributes
)
862 propertyNode
.map
["name"] = Node
{propertyName
};
863 propertyNode
.map
["attributes"] = Node
{propertyAttributes
};
864 propertiesNode
.array
.push_back(propertyNode
);
866 ma
->forEachObjCProperty(propertiesVMAddr
, vmAddrConverter
, visitProperty
);
867 return propertiesNode
.array
.empty() ? std::optional
<Node
>() : propertiesNode
;
870 auto getClassProtocols
= ^(const dyld3::MachOAnalyzer
* ma
, uint64_t protocolsVMAddr
,
871 const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) {
872 __block Node protocolsNode
;
874 auto visitProtocol
= ^(uint64_t protocolVMAddr
, const dyld3::MachOAnalyzer::ObjCProtocol
& protocol
) {
875 if (const char *name
= getString(ma
, protocol
.nameVMAddr
)) {
876 protocolsNode
.array
.push_back(Node
{name
});
880 ma
->forEachObjCProtocol(protocolsVMAddr
, vmAddrConverter
, visitProtocol
);
882 return protocolsNode
.array
.empty() ? std::optional
<Node
>() : protocolsNode
;
885 auto getProtocols
= ^(const dyld3::MachOAnalyzer
* ma
,
886 const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) {
887 __block Node protocols
;
889 auto getMethods
= ^(const dyld3::MachOAnalyzer
* ma
, uint64_t methodListVMAddr
, const std::string
&prefix
, Node
&node
){
890 auto visitMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
891 if (auto name
= getString(ma
, method
.nameVMAddr
)) {
892 node
.array
.push_back(Node
{prefix
+ name
});
896 ma
->forEachObjCMethod(methodListVMAddr
, vmAddrConverter
, visitMethod
);
899 auto visitProtocol
= ^(Diagnostics
& diag
, uint64_t protoVMAddr
,
900 const dyld3::MachOAnalyzer::ObjCProtocol
& objcProto
) {
901 const char* protoName
= getString(ma
, objcProto
.nameVMAddr
);
906 entry
.map
["protocolName"] = Node
{protoName
};
908 if ( objcProto
.protocolsVMAddr
!= 0 ) {
909 __block Node protocols
;
911 auto visitProtocol
= ^(uint64_t protocolRefVMAddr
, const dyld3::MachOAnalyzer::ObjCProtocol
&protocol
) {
912 if (auto name
= getString(ma
, protocol
.nameVMAddr
)) {
913 protocols
.array
.push_back(Node
{name
});
917 ma
->forEachObjCProtocol(objcProto
.protocolsVMAddr
, vmAddrConverter
, visitProtocol
);
918 if (!protocols
.array
.empty()) {
919 entry
.map
["protocols"] = protocols
;
924 getMethods(ma
, objcProto
.instanceMethodsVMAddr
, instancePrefix
, methods
);
925 getMethods(ma
, objcProto
.classMethodsVMAddr
, classPrefix
, methods
);
926 if (!methods
.array
.empty()) {
927 entry
.map
["methods"] = methods
;
931 getMethods(ma
, objcProto
.optionalInstanceMethodsVMAddr
, instancePrefix
, optMethods
);
932 getMethods(ma
, objcProto
.optionalClassMethodsVMAddr
, classPrefix
, optMethods
);
933 if (!optMethods
.array
.empty()) {
934 entry
.map
["optionalMethods"] = optMethods
;
937 protocols
.array
.push_back(entry
);
941 ma
->forEachObjCProtocol(diag
, vmAddrConverter
, visitProtocol
);
943 return protocols
.array
.empty() ? std::optional
<Node
>() : protocols
;
946 auto getSelRefs
= ^(const dyld3::MachOAnalyzer
* ma
,
947 const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) {
948 __block
std::vector
<const char *> selNames
;
950 auto visitSelRef
= ^(uint64_t selRefVMAddr
, uint64_t selRefTargetVMAddr
) {
951 if (auto selValue
= getString(ma
, selRefTargetVMAddr
)) {
952 selNames
.push_back(selValue
);
957 ma
->forEachObjCSelectorReference(diag
, vmAddrConverter
, visitSelRef
);
959 std::sort(selNames
.begin(), selNames
.end(),
960 [](const char* a
, const char* b
) {
961 return strcasecmp(a
, b
) < 0;
965 for (auto s
: selNames
) {
966 selrefs
.array
.push_back(Node
{s
});
969 return selrefs
.array
.empty() ? std::optional
<Node
>() : selrefs
;
972 auto getClasses
= ^(const dyld3::MachOAnalyzer
* ma
,
973 const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) {
975 const uint32_t pointerSize
= ma
->pointerSize();
977 // Get the vmAddrs for all exported symbols as we want to know if classes
979 std::set
<uint64_t> exportedSymbolVMAddrs
;
981 uint64_t loadAddress
= ma
->preferredLoadAddress();
983 uint32_t exportTrieRuntimeOffset
;
984 uint32_t exportTrieSize
;
985 if ( ma
->hasExportTrie(exportTrieRuntimeOffset
, exportTrieSize
) ) {
986 const uint8_t* start
= (uint8_t*)ma
+ exportTrieRuntimeOffset
;
987 const uint8_t* end
= start
+ exportTrieSize
;
988 std::vector
<ExportInfoTrie::Entry
> exports
;
989 if ( ExportInfoTrie::parseTrie(start
, end
, exports
) ) {
990 for (const ExportInfoTrie::Entry
& entry
: exports
) {
991 exportedSymbolVMAddrs
.insert(loadAddress
+ entry
.info
.address
);
997 __block Node classesNode
;
998 __block
bool skippedPreviousClass
= false;
999 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
1000 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
1001 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
1003 if (skippedPreviousClass
) {
1004 // If the class was bad, then skip the meta class too
1005 skippedPreviousClass
= false;
1009 skippedPreviousClass
= true;
1012 std::string classType
= "-";
1015 dyld3::MachOAnalyzer::PrintableStringResult classNameResult
;
1016 const char* className
= ma
->getPrintableString(objcClass
.nameVMAddr(pointerSize
), classNameResult
);
1017 if (classNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
) {
1021 const char* superClassName
= nullptr;
1022 if ( ma
->inDyldCache() ) {
1023 if ( objcClass
.superclassVMAddr
!= 0 ) {
1025 // If we are root class, then our superclass should actually point to our own class
1026 const uint32_t RO_ROOT
= (1<<1);
1027 if ( objcClass
.flags(pointerSize
) & RO_ROOT
) {
1028 auto it
= classVMAddrToName
.find(objcClass
.superclassVMAddr
);
1029 assert(it
!= classVMAddrToName
.end());
1030 superClassName
= it
->second
;
1032 auto it
= metaclassVMAddrToName
.find(objcClass
.superclassVMAddr
);
1033 assert(it
!= metaclassVMAddrToName
.end());
1034 superClassName
= it
->second
;
1037 auto it
= classVMAddrToName
.find(objcClass
.superclassVMAddr
);
1038 assert(it
!= classVMAddrToName
.end());
1039 superClassName
= it
->second
;
1043 // On-disk binary. Lets crack the chain to work out what we are pointing at
1044 dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup
;
1045 fixup
.raw64
= objcClass
.superclassVMAddr
;
1046 if (fixup
.arm64e
.bind
.bind
) {
1047 uint64_t bindOrdinal
= fixup
.arm64e
.authBind
.auth
? fixup
.arm64e
.authBind
.ordinal
: fixup
.arm64e
.bind
.ordinal
;
1048 // Bind to another image. Use the bind table to work out which name to bind to
1049 const char* symbolName
= onDiskChainedFixupBindTargets
[bindOrdinal
];
1051 if ( strstr(symbolName
, "_OBJC_METACLASS_$_") == symbolName
) {
1052 superClassName
= symbolName
+ strlen("_OBJC_METACLASS_$_");
1054 // Swift classes don't start with these prefixes so just skip them
1055 if (objcClass
.isSwiftLegacy
|| objcClass
.isSwiftStable
)
1059 if ( strstr(symbolName
, "_OBJC_CLASS_$_") == symbolName
) {
1060 superClassName
= symbolName
+ strlen("_OBJC_CLASS_$_");
1062 // Swift classes don't start with these prefixes so just skip them
1063 if (objcClass
.isSwiftLegacy
|| objcClass
.isSwiftStable
)
1068 // Rebase within this image.
1070 auto it
= onDiskMetaclassVMAddrToName
.find(objcClass
.superclassVMAddr
);
1071 assert(it
!= onDiskMetaclassVMAddrToName
.end());
1072 superClassName
= it
->second
;
1074 auto it
= onDiskClassVMAddrToName
.find(objcClass
.superclassVMAddr
);
1075 assert(it
!= onDiskClassVMAddrToName
.end());
1076 superClassName
= it
->second
;
1081 // Print the methods on this class
1082 __block Node methodsNode
;
1083 auto visitMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1084 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult
;
1085 const char* methodName
= ma
->getPrintableString(method
.nameVMAddr
, methodNameResult
);
1086 if (methodNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
1088 methodsNode
.array
.push_back(Node
{classType
+ methodName
});
1090 ma
->forEachObjCMethod(objcClass
.baseMethodsVMAddr(pointerSize
), vmAddrConverter
,
1093 std::optional
<Node
> properties
= getProperties(ma
, objcClass
.basePropertiesVMAddr(pointerSize
), vmAddrConverter
);
1096 assert(!classesNode
.array
.empty());
1097 Node
& currentClassNode
= classesNode
.array
.back();
1098 assert(currentClassNode
.map
["className"].value
== className
);
1099 if (!methodsNode
.array
.empty()) {
1100 Node
& currentMethodsNode
= currentClassNode
.map
["methods"];
1101 currentMethodsNode
.array
.insert(currentMethodsNode
.array
.end(),
1102 methodsNode
.array
.begin(),
1103 methodsNode
.array
.end());
1105 if (properties
.has_value()) {
1106 Node
& currentPropertiesNode
= currentClassNode
.map
["properties"];
1107 currentPropertiesNode
.array
.insert(currentPropertiesNode
.array
.end(),
1108 properties
->array
.begin(),
1109 properties
->array
.end());
1114 Node currentClassNode
;
1115 currentClassNode
.map
["className"] = Node
{className
};
1116 if ( superClassName
!= nullptr )
1117 currentClassNode
.map
["superClassName"] = Node
{superClassName
};
1118 if (!methodsNode
.array
.empty())
1119 currentClassNode
.map
["methods"] = methodsNode
;
1120 if (properties
.has_value())
1121 currentClassNode
.map
["properties"] = properties
.value();
1122 if (std::optional
<Node
> protocols
= getClassProtocols(ma
, objcClass
.baseProtocolsVMAddr(pointerSize
), vmAddrConverter
))
1123 currentClassNode
.map
["protocols"] = protocols
.value();
1125 currentClassNode
.map
["exported"] = Node
{exportedSymbolVMAddrs
.count(classVMAddr
) != 0};
1127 // We didn't skip this class so mark it as such
1128 skippedPreviousClass
= false;
1130 classesNode
.array
.push_back(currentClassNode
);
1133 ma
->forEachObjCClass(diag
, vmAddrConverter
, visitClass
);
1134 return classesNode
.array
.empty() ? std::optional
<Node
>() : classesNode
;
1137 auto getCategories
= ^(const dyld3::MachOAnalyzer
* ma
,
1138 const dyld3::MachOAnalyzer::VMAddrConverter
& vmAddrConverter
) {
1141 __block Node categoriesNode
;
1142 auto visitCategory
= ^(Diagnostics
& diag
, uint64_t categoryVMAddr
,
1143 const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) {
1144 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult
;
1145 const char* categoryName
= ma
->getPrintableString(objcCategory
.nameVMAddr
, categoryNameResult
);
1146 if (categoryNameResult
!= dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
)
1149 const char* className
= nullptr;
1150 if ( ma
->inDyldCache() ) {
1151 auto it
= classVMAddrToName
.find(objcCategory
.clsVMAddr
);
1152 assert(it
!= classVMAddrToName
.end());
1153 className
= it
->second
;
1155 // On-disk binary. Lets crack the chain to work out what we are pointing at
1156 dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup
;
1157 fixup
.raw64
= objcCategory
.clsVMAddr
;
1158 if (fixup
.arm64e
.bind
.bind
) {
1159 uint64_t bindOrdinal
= fixup
.arm64e
.authBind
.auth
? fixup
.arm64e
.authBind
.ordinal
: fixup
.arm64e
.bind
.ordinal
;
1160 // Bind to another image. Use the bind table to work out which name to bind to
1161 const char* symbolName
= onDiskChainedFixupBindTargets
[bindOrdinal
];
1162 if ( strstr(symbolName
, "_OBJC_CLASS_$_") == symbolName
) {
1163 className
= symbolName
+ strlen("_OBJC_CLASS_$_");
1165 // Swift classes don't start with these prefixes so just skip them
1166 // We don't know that this is a Swift class/category though, but skip it anyway
1170 auto it
= onDiskClassVMAddrToName
.find(objcCategory
.clsVMAddr
);
1171 if (it
== onDiskClassVMAddrToName
.end()) {
1172 // This is an odd binary with perhaps a Swift class. Just skip this entry
1175 className
= it
->second
;
1179 // Print the instance methods on this category
1180 __block Node methodsNode
;
1181 auto visitInstanceMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1182 if (auto methodName
= getString(ma
, method
.nameVMAddr
))
1183 methodsNode
.array
.push_back(Node
{instancePrefix
+ methodName
});
1185 ma
->forEachObjCMethod(objcCategory
.instanceMethodsVMAddr
, vmAddrConverter
,
1186 visitInstanceMethod
);
1188 // Print the instance methods on this category
1189 __block Node classMethodsNode
;
1190 auto visitClassMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1191 if (auto methodName
= getString(ma
, method
.nameVMAddr
))
1192 methodsNode
.array
.push_back(Node
{classPrefix
+ methodName
});
1194 ma
->forEachObjCMethod(objcCategory
.classMethodsVMAddr
, vmAddrConverter
,
1197 Node currentCategoryNode
;
1198 currentCategoryNode
.map
["categoryName"] = Node
{categoryName
};
1199 currentCategoryNode
.map
["className"] = Node
{className
};
1200 if (!methodsNode
.array
.empty())
1201 currentCategoryNode
.map
["methods"] = methodsNode
;
1202 if (std::optional
<Node
> properties
= getProperties(ma
, objcCategory
.instancePropertiesVMAddr
, vmAddrConverter
))
1203 currentCategoryNode
.map
["properties"] = properties
.value();
1204 if (std::optional
<Node
> protocols
= getClassProtocols(ma
, objcCategory
.protocolsVMAddr
, vmAddrConverter
))
1205 currentCategoryNode
.map
["protocols"] = protocols
.value();
1207 categoriesNode
.array
.push_back(currentCategoryNode
);
1210 ma
->forEachObjCCategory(diag
, vmAddrConverter
, visitCategory
);
1211 return categoriesNode
.array
.empty() ? std::optional
<Node
>() : categoriesNode
;
1214 __block
bool needsComma
= false;
1216 dyld3::json::streamArrayBegin(needsComma
);
1218 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
1219 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= dyldCache
->makeVMAddrConverter(rebased
);
1220 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
1223 imageRecord
.map
["imagePath"] = Node
{installName
};
1224 imageRecord
.map
["imageType"] = Node
{"cache-dylib"};
1225 std::optional
<Node
> classes
= getClasses(ma
, vmAddrConverter
);
1226 std::optional
<Node
> categories
= getCategories(ma
, vmAddrConverter
);
1227 std::optional
<Node
> protocols
= getProtocols(ma
, vmAddrConverter
);
1228 std::optional
<Node
> selrefs
= getSelRefs(ma
, vmAddrConverter
);
1230 // Skip emitting images with no objc data
1231 if (!classes
.has_value() && !categories
.has_value() && !protocols
.has_value() && !selrefs
.has_value())
1233 if (classes
.has_value())
1234 imageRecord
.map
["classes"] = classes
.value();
1235 if (categories
.has_value())
1236 imageRecord
.map
["categories"] = categories
.value();
1237 if (protocols
.has_value())
1238 imageRecord
.map
["protocols"] = protocols
.value();
1239 if (selrefs
.has_value())
1240 imageRecord
.map
["selrefs"] = selrefs
.value();
1242 dyld3::json::streamArrayNode(needsComma
, imageRecord
);
1245 FileSystemPhysical fileSystem
;
1246 dyld3::Platform platform
= dyldCache
->platform();
1247 const dyld3::GradedArchs
& archs
= dyld3::GradedArchs::forName(dyldCache
->archName(), true);
1249 dyldCache
->forEachLaunchClosure(^(const char *executableRuntimePath
, const dyld3::closure::LaunchClosure
*closure
) {
1251 char realerPath
[MAXPATHLEN
];
1252 dyld3::closure::LoadedFileInfo loadedFileInfo
= dyld3::MachOAnalyzer::load(diag
, fileSystem
, executableRuntimePath
, archs
, platform
, realerPath
);
1253 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)loadedFileInfo
.fileContent
;
1254 uint32_t pointerSize
= ma
->pointerSize();
1256 // Populate the bind targets for classes from other images
1257 onDiskChainedFixupBindTargets
.clear();
1258 ma
->forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
1259 onDiskChainedFixupBindTargets
.push_back(symbolName
);
1261 if ( diag
.hasError() )
1264 // Populate the rebase targets for class names
1265 onDiskMetaclassVMAddrToName
.clear();
1266 onDiskClassVMAddrToName
.clear();
1267 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
1268 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
1269 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
1270 if (auto className
= getString(ma
, objcClass
.nameVMAddr(pointerSize
))) {
1272 onDiskMetaclassVMAddrToName
[classVMAddr
] = className
;
1274 onDiskClassVMAddrToName
[classVMAddr
] = className
;
1278 // Get a vmAddrConverter for this on-disk binary. We can't use the shared cache one
1279 dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter
= ma
->makeVMAddrConverter(rebased
);
1281 ma
->forEachObjCClass(diag
, onDiskVMAddrConverter
, visitClass
);
1284 imageRecord
.map
["imagePath"] = Node
{executableRuntimePath
};
1285 imageRecord
.map
["imageType"] = Node
{"executable"};
1286 std::optional
<Node
> classes
= getClasses(ma
, onDiskVMAddrConverter
);
1287 std::optional
<Node
> categories
= getCategories(ma
, onDiskVMAddrConverter
);
1289 std::optional
<Node
> selrefs
= getSelRefs(ma
, onDiskVMAddrConverter
);
1291 // Skip emitting images with no objc data
1292 if (!classes
.has_value() && !categories
.has_value() && !selrefs
.has_value())
1294 if (classes
.has_value())
1295 imageRecord
.map
["classes"] = classes
.value();
1296 if (categories
.has_value())
1297 imageRecord
.map
["categories"] = categories
.value();
1298 if (selrefs
.has_value())
1299 imageRecord
.map
["selrefs"] = selrefs
.value();
1301 dyld3::json::streamArrayNode(needsComma
, imageRecord
);
1304 dyldCache
->forEachDlopenImage(^(const char *runtimePath
, const dyld3::closure::Image
*image
) {
1306 char realerPath
[MAXPATHLEN
];
1307 dyld3::closure::LoadedFileInfo loadedFileInfo
= dyld3::MachOAnalyzer::load(diag
, fileSystem
, runtimePath
, archs
, platform
, realerPath
);
1308 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)loadedFileInfo
.fileContent
;
1309 uint32_t pointerSize
= ma
->pointerSize();
1311 // Populate the bind targets for classes from other images
1312 onDiskChainedFixupBindTargets
.clear();
1313 ma
->forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
1314 onDiskChainedFixupBindTargets
.push_back(symbolName
);
1316 if ( diag
.hasError() )
1319 // Populate the rebase targets for class names
1320 onDiskMetaclassVMAddrToName
.clear();
1321 onDiskClassVMAddrToName
.clear();
1322 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
1323 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
1324 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
1325 if (auto className
= getString(ma
, objcClass
.nameVMAddr(pointerSize
))) {
1327 onDiskMetaclassVMAddrToName
[classVMAddr
] = className
;
1329 onDiskClassVMAddrToName
[classVMAddr
] = className
;
1333 // Get a vmAddrConverter for this on-disk binary. We can't use the shared cache one
1334 dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter
= ma
->makeVMAddrConverter(rebased
);
1336 ma
->forEachObjCClass(diag
, onDiskVMAddrConverter
, visitClass
);
1339 imageRecord
.map
["imagePath"] = Node
{runtimePath
};
1340 imageRecord
.map
["imageType"] = Node
{"non-cache-dylib"};
1341 std::optional
<Node
> classes
= getClasses(ma
, onDiskVMAddrConverter
);
1342 std::optional
<Node
> categories
= getCategories(ma
, onDiskVMAddrConverter
);
1344 std::optional
<Node
> selrefs
= getSelRefs(ma
, onDiskVMAddrConverter
);
1346 // Skip emitting images with no objc data
1347 if (!classes
.has_value() && !categories
.has_value() && !selrefs
.has_value())
1349 if (classes
.has_value())
1350 imageRecord
.map
["classes"] = classes
.value();
1351 if (categories
.has_value())
1352 imageRecord
.map
["categories"] = categories
.value();
1353 if (selrefs
.has_value())
1354 imageRecord
.map
["selrefs"] = selrefs
.value();
1356 dyld3::json::streamArrayNode(needsComma
, imageRecord
);
1359 dyld3::json::streamArrayEnd(needsComma
);
1361 else if ( options
.mode
== modeObjCSelectors
) {
1362 if ( dyldCache
->objcOpt() == nullptr ) {
1363 fprintf(stderr
, "Error: could not get optimized objc\n");
1366 const objc_opt::objc_selopt_t
* selectors
= dyldCache
->objcOpt()->selopt();
1367 if ( selectors
== nullptr ) {
1368 fprintf(stderr
, "Error: could not get optimized objc selectors\n");
1372 std::vector
<const char*> selNames
;
1373 for (uint64_t index
= 0; index
!= selectors
->capacity
; ++index
) {
1374 objc_opt::objc_stringhash_offset_t offset
= selectors
->offsets()[index
];
1377 const char* selName
= selectors
->getEntryForIndex((uint32_t)index
);
1378 selNames
.push_back(selName
);
1381 std::sort(selNames
.begin(), selNames
.end(),
1382 [](const char* a
, const char* b
) {
1383 // Sort by offset, not string value
1387 dyld3::json::Node root
;
1388 for (const char* selName
: selNames
) {
1389 dyld3::json::Node selNode
;
1390 selNode
.map
["selectorName"] = dyld3::json::Node
{selName
};
1391 selNode
.map
["offset"] = dyld3::json::Node
{(int64_t)selName
- (int64_t)dyldCache
};
1393 root
.array
.push_back(selNode
);
1396 dyld3::json::printJSON(root
, 0, std::cout
);
1398 else if ( options
.mode
== modeExtract
) {
1399 return dyld_shared_cache_extract_dylibs(sharedCachePath
, options
.extractionDir
);
1401 else if ( options
.mode
== modeObjCImpCaches
) {
1402 if (sharedCachePath
== nullptr) {
1403 fprintf(stderr
, "Cannot emit imp caches with live cache. Run again with the path to the cache file\n");
1406 __block
std::map
<uint64_t, const char*> methodToClassMap
;
1407 __block
std::map
<uint64_t, const char*> classVMAddrToNameMap
;
1408 const bool contentRebased
= false;
1409 const uint32_t pointerSize
= 8;
1411 // Get the base pointers from the magic section in objc
1412 __block
uint64_t objcCacheOffsetsSize
= 0;
1413 __block
const void* objcCacheOffsets
= nullptr;
1414 __block Diagnostics diag
;
1415 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1416 if ( !strcmp(installName
, "/usr/lib/libobjc.A.dylib") ) {
1417 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
1418 objcCacheOffsets
= ma
->findSectionContent("__DATA_CONST", "__objc_scoffs", objcCacheOffsetsSize
);
1422 if ( objcCacheOffsets
== nullptr ) {
1423 fprintf(stderr
, "Unable to print imp-caches as cannot find __DATA_CONST __objc_scoffs inside /usr/lib/libobjc.A.dylib\n");
1427 if ( objcCacheOffsetsSize
< (4 * pointerSize
) ) {
1428 fprintf(stderr
, "Unable to print imp-caches as __DATA_CONST __objc_scoffs is too small (%lld vs required %u)\n", objcCacheOffsetsSize
, (4 * pointerSize
));
1432 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter
= dyldCache
->makeVMAddrConverter(contentRebased
);
1434 uint64_t selectorStringVMAddrStart
= vmAddrConverter
.convertToVMAddr(((uint64_t*)objcCacheOffsets
)[0]);
1435 uint64_t selectorStringVMAddrEnd
= vmAddrConverter
.convertToVMAddr(((uint64_t*)objcCacheOffsets
)[1]);
1437 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
1438 if (diag
.hasError())
1441 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
1442 intptr_t slide
= ma
->getSlide();
1444 ma
->forEachObjCClass(diag
, vmAddrConverter
, ^(Diagnostics
& diag
,
1445 uint64_t classVMAddr
,
1446 uint64_t classSuperclassVMAddr
,
1447 uint64_t classDataVMAddr
,
1448 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
,
1450 const char* className
= (const char*)objcClass
.nameVMAddr(pointerSize
) + slide
;
1451 classVMAddrToNameMap
[classVMAddr
] = className
;
1452 ma
->forEachObjCMethod(objcClass
.baseMethodsVMAddr(pointerSize
), vmAddrConverter
,
1453 ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1454 // const char* methodName = (const char*)(method.nameVMAddr + slide);
1455 methodToClassMap
[method
.impVMAddr
] = className
;
1459 ma
->forEachObjCCategory(diag
, vmAddrConverter
, ^(Diagnostics
& diag
, uint64_t categoryVMAddr
, const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) {
1460 ma
->forEachObjCMethod(objcCategory
.instanceMethodsVMAddr
, vmAddrConverter
, ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1461 const char* catName
= (const char*)objcCategory
.nameVMAddr
+ slide
;
1462 // const char* methodName = (const char*)(method.nameVMAddr + slide);
1463 methodToClassMap
[method
.impVMAddr
] = catName
;
1466 ma
->forEachObjCMethod(objcCategory
.classMethodsVMAddr
, vmAddrConverter
, ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
1467 const char* catName
= (const char*)objcCategory
.nameVMAddr
+ slide
;
1468 // const char* methodName = (const char*)(method.nameVMAddr + slide);
1469 methodToClassMap
[method
.impVMAddr
] = catName
;
1473 if (diag
.hasError())
1476 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
1477 if (diag
.hasError())
1480 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
1481 intptr_t slide
= ma
->getSlide();
1483 ma
->forEachObjCClass(diag
, vmAddrConverter
, ^(Diagnostics
& diag
,
1484 uint64_t classVMAddr
,
1485 uint64_t classSuperclassVMAddr
,
1486 uint64_t classDataVMAddr
,
1487 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
,
1489 const char* type
= "class";
1491 type
= "meta-class";
1492 const char* className
= (const char*)objcClass
.nameVMAddr(pointerSize
) + slide
;
1494 if (objcClass
.methodCacheVMAddr
== 0) {
1495 printf("%s (%s): empty\n", className
, type
);
1504 int32_t fallback_class_offset
;
1505 uint32_t cache_shift
: 5;
1506 uint32_t cache_mask
: 11;
1507 uint32_t occupied
: 14;
1508 uint32_t has_inlines
: 1;
1509 uint32_t bit_one
: 1;
1510 struct Bucket buckets
[];
1513 const ImpCache
* impCache
= (const ImpCache
*)(objcClass
.methodCacheVMAddr
+ slide
);
1514 printf("%s (%s): %d buckets\n", className
, type
, impCache
->cache_mask
+ 1);
1516 if ((classVMAddr
+ impCache
->fallback_class_offset
) != objcClass
.superclassVMAddr
) {
1517 printf("Flattening fallback: %s\n", classVMAddrToNameMap
[classVMAddr
+ impCache
->fallback_class_offset
]);
1519 // Buckets are a 32-bit offset from the impcache itself
1520 for (uint32_t i
= 0; i
<= impCache
->cache_mask
; ++i
) {
1521 const Bucket
& b
= impCache
->buckets
[i
];
1522 uint64_t sel
= (uint64_t)b
.selOffset
+ selectorStringVMAddrStart
;
1523 uint64_t imp
= classVMAddr
- (uint64_t)b
.impOffset
;
1524 if (b
.selOffset
== 0xFFFFFFFF) {
1526 printf(" - 0x%016llx: %s\n", 0ULL, "");
1528 assert(sel
< selectorStringVMAddrEnd
);
1530 auto it
= methodToClassMap
.find(imp
);
1531 if (it
== methodToClassMap
.end()) {
1532 fprintf(stderr
, "Could not find IMP %llx (for %s)\n", imp
, (const char*)(sel
+ slide
));
1534 assert(it
!= methodToClassMap
.end());
1535 printf(" - 0x%016llx: %s (from %s)\n", imp
, (const char*)(sel
+ slide
), it
->second
);
1541 switch ( options
.mode
) {
1543 // list all dylibs, including their aliases (symlinks to them) with option vmaddr
1544 __block
std::vector
<std::unordered_set
<std::string
>> indexToPaths
;
1545 __block
std::vector
<uint64_t> indexToAddr
;
1546 __block
std::vector
<std::string
> indexToUUID
;
1547 dyldCache
->forEachImageTextSegment(^(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const unsigned char* dylibUUID
, const char* installName
, bool& stop
) {
1548 std::unordered_set
<std::string
> empty
;
1549 if ( options
.printVMAddrs
)
1550 indexToAddr
.push_back(loadAddressUnslid
);
1551 if ( options
.printUUIDs
) {
1552 uuid_string_t uuidString
;
1553 uuid_unparse_upper(dylibUUID
, uuidString
);
1554 indexToUUID
.push_back(uuidString
);
1556 indexToPaths
.push_back(empty
);
1557 indexToPaths
.back().insert(installName
);
1559 dyldCache
->forEachDylibPath(^(const char* dylibPath
, uint32_t index
) {
1560 indexToPaths
[index
].insert(dylibPath
);
1563 for (const std::unordered_set
<std::string
>& paths
: indexToPaths
) {
1564 for (const std::string
& path
: paths
) {
1565 if ( options
.printVMAddrs
)
1566 printf("0x%08llX ", indexToAddr
[index
]);
1567 if ( options
.printUUIDs
)
1568 printf("<%s> ", indexToUUID
[index
].c_str());
1569 printf("%s\n", path
.c_str());
1575 case modeListDylibsWithSection
: {
1576 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1577 dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)mh
;
1578 mf
->forEachSection(^(const dyld3::MachOFile::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
1579 if ( (strcmp(sectInfo
.sectName
, options
.sectionName
) == 0) && (strcmp(sectInfo
.segInfo
.segName
, options
.segmentName
) == 0) ) {
1580 printf("%s\n", installName
);
1588 __block
std::map
<uint64_t, const char*> dataSegNames
;
1589 __block
std::map
<uint64_t, uint64_t> dataSegEnds
;
1590 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1591 dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)mh
;
1592 mf
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo
&info
, bool &stop
) {
1593 printf("0x%08llX - 0x%08llX %s %s\n", info
.vmAddr
, info
.vmAddr
+ info
.vmSize
, info
.segName
, installName
);
1594 if ( strncmp(info
.segName
, "__DATA", 6) == 0 ) {
1595 dataSegNames
[info
.vmAddr
] = installName
;
1596 dataSegEnds
[info
.vmAddr
] = info
.vmAddr
+ info
.vmSize
;
1600 // <rdar://problem/51084507> Enhance dyld_shared_cache_util to show where section alignment added padding
1601 uint64_t lastEnd
= 0;
1602 for (const auto& entry
: dataSegEnds
) {
1603 uint64_t padding
= entry
.first
- lastEnd
;
1604 if ( (padding
> 32) && (lastEnd
!= 0) ) {
1605 printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd
, entry
.first
, padding
/1024);
1607 lastEnd
= entry
.second
;
1611 case modeDependencies
: {
1612 __block
bool dependentTargetFound
= false;
1613 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1614 if ( strcmp(options
.dependentsOfPath
, installName
) != 0 )
1616 dependentTargetFound
= true;
1618 auto printDep
= [&options
](const char *loadPath
, uint32_t compatVersion
, uint32_t curVersion
) {
1619 if ( options
.printDylibVersions
) {
1620 uint32_t compat_vers
= compatVersion
;
1621 uint32_t current_vers
= curVersion
;
1622 printf("\t%s", loadPath
);
1623 if ( compat_vers
!= 0xFFFFFFFF ) {
1624 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
1625 (compat_vers
>> 16),
1626 (compat_vers
>> 8) & 0xff,
1627 (compat_vers
) & 0xff,
1628 (current_vers
>> 16),
1629 (current_vers
>> 8) & 0xff,
1630 (current_vers
) & 0xff);
1637 printf("\t%s\n", loadPath
);
1641 dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)mh
;
1643 // First print out our dylib and version.
1644 const char* dylibInstallName
;
1645 uint32_t currentVersion
;
1646 uint32_t compatVersion
;
1647 if ( mf
->getDylibInstallName(&dylibInstallName
, &compatVersion
, ¤tVersion
) ) {
1648 printDep(dylibInstallName
, compatVersion
, currentVersion
);
1651 // Then the dependent dylibs.
1652 mf
->forEachDependentDylib(^(const char *loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
1653 printDep(loadPath
, compatVersion
, curVersion
);
1656 if (options
.dependentsOfPath
&& !dependentTargetFound
) {
1657 fprintf(stderr
, "Error: could not find '%s' in the shared cache at\n %s\n", options
.dependentsOfPath
, sharedCachePath
);
1662 case modeLinkEdit
: {
1663 std::map
<uint32_t, const char*> pageToContent
;
1664 auto add_linkedit
= [&pageToContent
](uint32_t pageStart
, uint32_t pageEnd
, const char* message
) {
1665 for (uint32_t p
= pageStart
; p
<= pageEnd
; p
+= 4096) {
1666 std::map
<uint32_t, const char*>::iterator pos
= pageToContent
.find(p
);
1667 if ( pos
== pageToContent
.end() ) {
1668 pageToContent
[p
] = strdup(message
);
1671 const char* oldMessage
= pos
->second
;
1673 asprintf(&newMesssage
, "%s, %s", oldMessage
, message
);
1674 pageToContent
[p
] = newMesssage
;
1675 ::free((void*)oldMessage
);
1680 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1681 dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
1683 dyld3::MachOAnalyzer::LinkEditInfo leInfo
;
1684 ma
->getLinkEditPointers(diag
, leInfo
);
1686 if (diag
.hasError())
1690 const char* shortName
= strrchr(installName
, '/') + 1;
1691 // add export trie info
1692 if ( leInfo
.dyldInfo
->export_size
!= 0 ) {
1693 //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off());
1694 uint32_t exportPageOffsetStart
= leInfo
.dyldInfo
->export_off
& (-4096);
1695 uint32_t exportPageOffsetEnd
= (leInfo
.dyldInfo
->export_off
+ leInfo
.dyldInfo
->export_size
) & (-4096);
1696 sprintf(message
, "exports from %s", shortName
);
1697 add_linkedit(exportPageOffsetStart
, exportPageOffsetEnd
, message
);
1700 if ( leInfo
.dyldInfo
->bind_size
!= 0 ) {
1701 uint32_t bindPageOffsetStart
= leInfo
.dyldInfo
->bind_off
& (-4096);
1702 uint32_t bindPageOffsetEnd
= (leInfo
.dyldInfo
->bind_off
+ leInfo
.dyldInfo
->bind_size
) & (-4096);
1703 sprintf(message
, "bindings from %s", shortName
);
1704 add_linkedit(bindPageOffsetStart
, bindPageOffsetEnd
, message
);
1706 // add lazy binding info
1707 if ( leInfo
.dyldInfo
->lazy_bind_size
!= 0 ) {
1708 uint32_t lazybindPageOffsetStart
= leInfo
.dyldInfo
->lazy_bind_off
& (-4096);
1709 uint32_t lazybindPageOffsetEnd
= (leInfo
.dyldInfo
->lazy_bind_off
+ leInfo
.dyldInfo
->lazy_bind_size
) & (-4096);
1710 sprintf(message
, "lazy bindings from %s", shortName
);
1711 add_linkedit(lazybindPageOffsetStart
, lazybindPageOffsetEnd
, message
);
1713 // add weak binding info
1714 if ( leInfo
.dyldInfo
->weak_bind_size
!= 0 ) {
1715 uint32_t weakbindPageOffsetStart
= leInfo
.dyldInfo
->weak_bind_off
& (-4096);
1716 uint32_t weakbindPageOffsetEnd
= (leInfo
.dyldInfo
->weak_bind_off
+ leInfo
.dyldInfo
->weak_bind_size
) & (-4096);
1717 sprintf(message
, "weak bindings from %s", shortName
);
1718 add_linkedit(weakbindPageOffsetStart
, weakbindPageOffsetEnd
, message
);
1722 for (std::map
<uint32_t, const char*>::iterator it
= pageToContent
.begin(); it
!= pageToContent
.end(); ++it
) {
1723 printf("0x%08X %s\n", it
->first
, it
->second
);
1732 __block
std::vector
<TextInfo
> textSegments
;
1733 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1735 dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
1736 ma
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo
&info
, bool &stop
) {
1737 if ( strcmp(info
.segName
, "__TEXT") != 0 )
1739 textSegments
.push_back({ info
.fileSize
, installName
});
1742 std::sort(textSegments
.begin(), textSegments
.end(), [](const TextInfo
& left
, const TextInfo
& right
) {
1743 return (left
.textSize
> right
.textSize
);
1745 for (std::vector
<TextInfo
>::iterator it
= textSegments
.begin(); it
!= textSegments
.end(); ++it
) {
1746 printf(" 0x%08llX %s\n", it
->textSize
, it
->path
);
1750 case modePatchTable
: {
1751 std::vector
<SegmentInfo
> segInfos
;
1752 buildSegmentInfo(dyldCache
, segInfos
);
1753 __block
uint32_t imageIndex
= 0;
1754 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
1755 printf("%s:\n", installName
);
1756 dyldCache
->forEachPatchableExport(imageIndex
, ^(uint32_t cacheOffsetOfImpl
, const char* exportName
) {
1757 printf(" export: 0x%08X %s\n", cacheOffsetOfImpl
, exportName
);
1758 dyldCache
->forEachPatchableUseOfExport(imageIndex
, cacheOffsetOfImpl
, ^(dyld_cache_patchable_location patchLocation
) {
1759 SegmentInfo usageAt
;
1760 const uint64_t patchLocVmAddr
= dyldCache
->unslidLoadAddress() + patchLocation
.cacheOffset
;
1761 findImageAndSegment(dyldCache
, segInfos
, patchLocation
.cacheOffset
, &usageAt
);
1762 if ( patchLocation
.addend
== 0 )
1763 printf(" used by: %s+0x%04llX in %s\n", usageAt
.segName
, patchLocVmAddr
-usageAt
.vmAddr
, usageAt
.installName
);
1765 printf(" used by: %s+0x%04llX (addend=%d) in %s\n", usageAt
.segName
, patchLocVmAddr
-usageAt
.vmAddr
, patchLocation
.addend
, usageAt
.installName
);
1775 case modeVerboseSlideInfo
:
1777 case modeLocalSymbols
:
1779 case modeJSONDependents
:
1780 case modeSectionSizes
:
1782 case modeObjCProtocols
:
1783 case modeObjCImpCaches
:
1784 case modeObjCClasses
:
1785 case modeObjCSelectors
: