dyld-851.27.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / dyld_shared_cache_util.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <string.h>
29 #include <dlfcn.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <sys/mman.h>
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>
42
43 #include <TargetConditionals.h>
44
45 #include <map>
46 #include <vector>
47 #include <iostream>
48 #include <optional>
49
50 #include "ClosureBuilder.h"
51 #include "DyldSharedCache.h"
52 #include "ClosureFileSystemPhysical.h"
53 #include "JSONWriter.h"
54 #include "Trie.hpp"
55 #include "dsc_extractor.h"
56
57 #include "objc-shared-cache.h"
58
59 #if TARGET_OS_OSX
60 #define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle"
61 #else
62 #define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle"
63 #endif
64
65 using dyld3::closure::ClosureBuilder;
66 using dyld3::closure::FileSystemPhysical;
67
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)
70 {
71 struct stat statbuf;
72 if ( ::stat(path, &statbuf) ) {
73 fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
74 return nullptr;
75 }
76
77 int cache_fd = ::open(path, O_RDONLY);
78 if (cache_fd < 0) {
79 fprintf(stderr, "Error: failed to open shared cache file at %s\n", path);
80 return nullptr;
81 }
82
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);
86 return nullptr;
87 }
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];
91
92 size_t vmSize = (size_t)(lastMapping->address + lastMapping->size - mappings[0].address);
93 vm_address_t result;
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);
97 return nullptr;
98 }
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);
104 return nullptr;
105 }
106 }
107 ::close(cache_fd);
108
109 return (DyldSharedCache*)result;
110 }
111
112 enum Mode {
113 modeNone,
114 modeList,
115 modeMap,
116 modeDependencies,
117 modeSlideInfo,
118 modeVerboseSlideInfo,
119 modeTextInfo,
120 modeLinkEdit,
121 modeLocalSymbols,
122 modeJSONMap,
123 modeJSONDependents,
124 modeSectionSizes,
125 modeStrings,
126 modeInfo,
127 modeSize,
128 modeObjCProtocols,
129 modeObjCImpCaches,
130 modeObjCClasses,
131 modeObjCSelectors,
132 modeExtract,
133 modePatchTable,
134 modeListDylibsWithSection
135 };
136
137 struct Options {
138 Mode mode;
139 const char* dependentsOfPath;
140 const char* extractionDir;
141 const char* segmentName;
142 const char* sectionName;
143 bool printUUIDs;
144 bool printVMAddrs;
145 bool printDylibVersions;
146 bool printInodes;
147 };
148
149
150 void usage() {
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");
152 }
153
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");
157 usage();
158 exit(1);
159 }
160 }
161
162 struct SegmentInfo
163 {
164 uint64_t vmAddr;
165 uint64_t vmSize;
166 const char* installName;
167 const char* segName;
168 };
169
170 static void buildSegmentInfo(const DyldSharedCache* dyldCache, std::vector<SegmentInfo>& segInfos)
171 {
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});
176 });
177 });
178
179 std::sort(segInfos.begin(), segInfos.end(), [](const SegmentInfo& l, const SegmentInfo& r) -> bool {
180 return l.vmAddr < r.vmAddr;
181 });
182 }
183
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) {
187
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]);
198 printf("\n");
199 }
200 }
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)
212 {
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;
218
219 uint32_t pageOffset = startOffset;
220 uint32_t delta = 1;
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);
226 if ( value != 0 ) {
227 value += valueAdd;
228 value += slideAmount;
229 }
230 printf(" [% 5d + 0x%04llX]: 0x%016llX = 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue, (uint64_t)value);
231 pageOffset += delta;
232 }
233 };
234 if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
235 printf("page[% 5d]: no rebasing\n", i);
236 }
237 else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
238 printf("page[% 5d]: ", i);
239 int j=(start & 0x3FFF);
240 bool done = false;
241 do {
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);
248 }
249 done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
250 ++j;
251 } while ( !done );
252 printf("\n");
253 }
254 else {
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);
260 }
261 }
262 }
263 }
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);
274 continue;
275 }
276
277 printf("page[% 5d]: start=0x%04X\n", i, delta);
278 if ( !verboseSlideInfo )
279 continue;
280
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;
284 do {
285 loc += delta;
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());
296 }
297 else {
298 uint64_t targetValue = ptr.arm64e.unpackTarget();
299 printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue);
300 }
301 } while (delta != 0);
302 }
303 }
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)
315 {
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;
321
322 uint32_t pageOffset = startOffset;
323 uint32_t delta = 1;
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
331 }
332 else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
333 // small negative non-pointer
334 value |= 0xC0000000;
335 }
336 else {
337 value += valueAdd;
338 value += slideAmount;
339 }
340 printf(" [% 5d + 0x%04X]: 0x%08X\n", i, pageOffset, rawValue);
341 pageOffset += delta;
342 }
343 };
344 if ( start == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
345 printf("page[% 5d]: no rebasing\n", i);
346 }
347 else if ( start & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
348 printf("page[% 5d]: ", i);
349 int j=(start & DYLD_CACHE_SLIDE4_PAGE_INDEX);
350 bool done = false;
351 do {
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);
358 }
359 done = (extras[j] & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
360 ++j;
361 } while ( !done );
362 printf("\n");
363 }
364 else {
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);
370 }
371 }
372 }
373 }
374 }
375
376
377 static void findImageAndSegment(const DyldSharedCache* dyldCache, const std::vector<SegmentInfo>& segInfos, uint32_t cacheOffset, SegmentInfo* found)
378 {
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;
384 });
385 *found = *lowIt;
386 }
387
388
389 int main (int argc, const char* argv[]) {
390
391 const char* sharedCachePath = nullptr;
392
393 Options options;
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;
401
402 bool printStrings = false;
403 bool printExports = false;
404
405 for (uint32_t i = 1; i < argc; i++) {
406 const char* opt = argv[i];
407 if (opt[0] == '-') {
408 if (strcmp(opt, "-list") == 0) {
409 checkMode(options.mode);
410 options.mode = modeList;
411 }
412 else if (strcmp(opt, "-dependents") == 0) {
413 checkMode(options.mode);
414 options.mode = modeDependencies;
415 options.dependentsOfPath = argv[++i];
416 if ( i >= argc ) {
417 fprintf(stderr, "Error: option -depdendents requires an argument\n");
418 usage();
419 exit(1);
420 }
421 }
422 else if (strcmp(opt, "-linkedit") == 0) {
423 checkMode(options.mode);
424 options.mode = modeLinkEdit;
425 }
426 else if (strcmp(opt, "-info") == 0) {
427 checkMode(options.mode);
428 options.mode = modeInfo;
429 }
430 else if (strcmp(opt, "-slide_info") == 0) {
431 checkMode(options.mode);
432 options.mode = modeSlideInfo;
433 }
434 else if (strcmp(opt, "-verbose_slide_info") == 0) {
435 checkMode(options.mode);
436 options.mode = modeVerboseSlideInfo;
437 }
438 else if (strcmp(opt, "-text_info") == 0) {
439 checkMode(options.mode);
440 options.mode = modeTextInfo;
441 }
442 else if (strcmp(opt, "-local_symbols") == 0) {
443 checkMode(options.mode);
444 options.mode = modeLocalSymbols;
445 }
446 else if (strcmp(opt, "-strings") == 0) {
447 if (options.mode != modeStrings)
448 checkMode(options.mode);
449 options.mode = modeStrings;
450 printStrings = true;
451 }
452 else if (strcmp(opt, "-sections") == 0) {
453 checkMode(options.mode);
454 options.mode = modeSectionSizes;
455 }
456 else if (strcmp(opt, "-exports") == 0) {
457 if (options.mode != modeStrings)
458 checkMode(options.mode);
459 options.mode = modeStrings;
460 printExports = true;
461 }
462 else if (strcmp(opt, "-map") == 0) {
463 checkMode(options.mode);
464 options.mode = modeMap;
465 }
466 else if (strcmp(opt, "-json-map") == 0) {
467 checkMode(options.mode);
468 options.mode = modeJSONMap;
469 }
470 else if (strcmp(opt, "-json-dependents") == 0) {
471 checkMode(options.mode);
472 options.mode = modeJSONDependents;
473 }
474 else if (strcmp(opt, "-size") == 0) {
475 checkMode(options.mode);
476 options.mode = modeSize;
477 }
478 else if (strcmp(opt, "-objc-protocols") == 0) {
479 checkMode(options.mode);
480 options.mode = modeObjCProtocols;
481 }
482 else if (strcmp(opt, "-objc-imp-caches") == 0) {
483 checkMode(options.mode);
484 options.mode = modeObjCImpCaches;
485 }
486 else if (strcmp(opt, "-objc-classes") == 0) {
487 checkMode(options.mode);
488 options.mode = modeObjCClasses;
489 }
490 else if (strcmp(opt, "-objc-selectors") == 0) {
491 checkMode(options.mode);
492 options.mode = modeObjCSelectors;
493 }
494 else if (strcmp(opt, "-extract") == 0) {
495 checkMode(options.mode);
496 options.mode = modeExtract;
497 options.extractionDir = argv[++i];
498 if ( i >= argc ) {
499 fprintf(stderr, "Error: option -extract requires a directory argument\n");
500 usage();
501 exit(1);
502 }
503 }
504 else if (strcmp(opt, "-uuid") == 0) {
505 options.printUUIDs = true;
506 }
507 else if (strcmp(opt, "-inode") == 0) {
508 options.printInodes = true;
509 }
510 else if (strcmp(opt, "-versions") == 0) {
511 options.printDylibVersions = true;
512 }
513 else if (strcmp(opt, "-vmaddr") == 0) {
514 options.printVMAddrs = true;
515 }
516 else if (strcmp(opt, "-patch_table") == 0) {
517 options.mode = modePatchTable;
518 }
519 else if (strcmp(opt, "-list_dylibs_with_section") == 0) {
520 options.mode = modeListDylibsWithSection;
521 options.segmentName = argv[++i];
522 options.sectionName = argv[++i];
523 if ( i >= argc ) {
524 fprintf(stderr, "Error: option -list_dylibs_with_section requires a segment and section name\n");
525 usage();
526 exit(1);
527 }
528 }
529 else {
530 fprintf(stderr, "Error: unrecognized option %s\n", opt);
531 usage();
532 exit(1);
533 }
534 }
535 else {
536 sharedCachePath = opt;
537 }
538 }
539
540 if ( options.mode == modeNone ) {
541 fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
542 usage();
543 exit(1);
544 }
545
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");
549
550 if ( options.printVMAddrs && (options.mode != modeList) )
551 fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
552
553 if ( options.printDylibVersions && (options.mode != modeDependencies) )
554 fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
555
556 if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) {
557 fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
558 usage();
559 exit(1);
560 }
561 }
562
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 )
568 return 1;
569 }
570 else {
571 size_t cacheLength;
572 dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
573 if (dyldCache == nullptr) {
574 fprintf(stderr, "Could not get in-memory shared cache\n");
575 return 1;
576 }
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");
579 return 1;
580 }
581 }
582
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");
586 exit(1);
587 }
588
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);
594 });
595 return 0;
596 }
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);
602
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);
610 }
611 if ( dyldCache->hasSlideInfo() ) {
612 uint32_t pageSize = 0x4000; // fix me for intel
613 uint32_t possibleSlideValues = (uint32_t)(header->maxSlide/pageSize);
614 uint32_t entropyBits = 0;
615 if ( possibleSlideValues > 1 )
616 entropyBits = __builtin_clz(possibleSlideValues - 1);
617 printf("ASLR entropy: %u-bits\n", entropyBits);
618 }
619 printf("mappings:\n");
620 dyldCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size,
621 uint32_t initProt, uint32_t maxProt, uint64_t flags) {
622 std::string mappingName = "";
623 if ( maxProt & VM_PROT_EXECUTE ) {
624 mappingName = "__TEXT";
625 } else if ( maxProt & VM_PROT_WRITE ) {
626 // Start off with __DATA or __AUTH
627 if ( flags & DYLD_CACHE_MAPPING_AUTH_DATA )
628 mappingName = "__AUTH";
629 else
630 mappingName = "__DATA";
631 // Then add one of "", _DIRTY, or _CONST
632 if ( flags & DYLD_CACHE_MAPPING_DIRTY_DATA )
633 mappingName += "_DIRTY";
634 else if ( flags & DYLD_CACHE_MAPPING_CONST_DATA )
635 mappingName += "_CONST";
636 }
637 else if ( maxProt & VM_PROT_READ ) {
638 mappingName = "__LINKEDIT";
639 } else {
640 mappingName = "*unknown*";
641 }
642 uint64_t fileOffset = (uint8_t*)content - (uint8_t*)dyldCache;
643 printf("%16s %4lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
644 mappingName.c_str(), size / (1024*1024), fileOffset, fileOffset + size, vmAddr, vmAddr + size);
645 });
646 if ( header->codeSignatureOffset != 0 ) {
647 uint64_t size = header->codeSignatureSize;
648 uint64_t csAddr = dyldCache->getCodeSignAddress();
649 if ( size != 0 )
650 printf("%16s %4lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
651 "code sign", size/(1024*1024), header->codeSignatureOffset, header->codeSignatureOffset + size, csAddr, csAddr + size);
652 }
653 dyldCache->forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart,
654 uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) {
655
656 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
657 slideInfoSize/1024, slideInfoOffset, slideInfoOffset + slideInfoSize);
658 });
659 if ( header->localSymbolsOffset != 0 )
660 printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
661 header->localSymbolsSize/(1024*1024), header->localSymbolsOffset, header->localSymbolsOffset + header->localSymbolsSize);
662 }
663 else if ( options.mode == modeTextInfo ) {
664 const dyld_cache_header* header = &dyldCache->header;
665 printf("dylib text infos (count=%llu):\n", header->imagesTextCount);
666 dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char *dylibUUID, const char *installName, bool &stop) {
667 uuid_string_t uuidString;
668 uuid_unparse_upper(dylibUUID, uuidString);
669 printf(" 0x%09llX -> 0x%09llX <%s> %s\n", loadAddressUnslid, loadAddressUnslid + textSegmentSize, uuidString, installName);
670 });
671 }
672 else if ( options.mode == modeLocalSymbols ) {
673 if ( !dyldCache->hasLocalSymbolsInfo() ) {
674 fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
675 exit(1);
676 }
677 const bool is64 = (strstr(dyldCache->archName(), "64") != NULL);
678 const uint32_t nlistFileOffset = (uint32_t)((uint8_t*)dyldCache->getLocalNlistEntries() - (uint8_t*)dyldCache);
679 const uint32_t nlistCount = dyldCache->getLocalNlistCount();
680 const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12;
681 const char* localStrings = dyldCache->getLocalStrings();
682 const uint32_t stringsFileOffset = (uint32_t)((uint8_t*)localStrings - (uint8_t*)dyldCache);
683 const uint32_t stringsSize = dyldCache->getLocalStringsSize();
684
685 printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize);
686 printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize);
687
688 __block uint32_t entriesCount = 0;
689 dyldCache->forEachLocalSymbolEntry(^(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool &stop) {
690 const char* imageName = dyldCache->getIndexedImagePath(entriesCount);
691 printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", nlistStartIndex, nlistCount, imageName);
692 #if 0
693 if ( is64 ) {
694 const nlist_64* symTab = (nlist_64*)((char*)dyldCache + nlistFileOffset);
695 for (int e = 0; e < nlistCount; ++e) {
696 const nlist_64* entry = &symTab[nlistStartIndex + e];
697 printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &localStrings[entry->n_un.n_strx]);
698 printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value);
699 }
700 }
701 #endif
702 entriesCount++;
703 });
704 printf("local symbols by dylib (count=%d):\n", entriesCount);
705 }
706 else if ( options.mode == modeJSONMap ) {
707 std::string buffer = dyldCache->generateJSONMap("unknown");
708 printf("%s\n", buffer.c_str());
709 }
710 else if ( options.mode == modeJSONDependents ) {
711 std::cout << dyldCache->generateJSONDependents();
712 }
713 else if ( options.mode == modeStrings ) {
714 if (printStrings) {
715 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
716 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
717 int64_t slide = ma->getSlide();
718 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
719 if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
720 if ( malformedSectionRange ) {
721 stop = true;
722 return;
723 }
724 const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
725 const char* s = (char*)content;
726 const char* end = s + info.sectSize;
727 while ( s < end ) {
728 printf("%s: %s\n", ma->installName(), s);
729 while (*s != '\0' )
730 ++s;
731 ++s;
732 }
733 }
734 });
735 });
736 }
737
738 if (printExports) {
739 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
740 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
741 uint32_t exportTrieRuntimeOffset;
742 uint32_t exportTrieSize;
743 if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) {
744 const uint8_t* start = (uint8_t*)mh + exportTrieRuntimeOffset;
745 const uint8_t* end = start + exportTrieSize;
746 std::vector<ExportInfoTrie::Entry> exports;
747 if ( !ExportInfoTrie::parseTrie(start, end, exports) ) {
748 return;
749 }
750
751 for (const ExportInfoTrie::Entry& entry: exports) {
752 printf("%s: %s\n", ma->installName(), entry.name.c_str());
753 }
754 }
755 });
756 }
757 }
758 else if ( options.mode == modeSectionSizes ) {
759 __block std::map<std::string, uint64_t> sectionSizes;
760 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
761 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
762 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
763 std::string section = std::string(sectInfo.segInfo.segName) + " " + sectInfo.sectName;
764 sectionSizes[section] += sectInfo.sectSize;
765 });
766 });
767 for (const auto& keyAndValue : sectionSizes) {
768 printf("%lld %s\n", keyAndValue.second, keyAndValue.first.c_str());
769 }
770 }
771 else if ( options.mode == modeObjCProtocols ) {
772 if ( dyldCache->objcOpt() == nullptr ) {
773 fprintf(stderr, "Error: could not get optimized objc\n");
774 return 1;
775 }
776 objc_opt::objc_protocolopt2_t* protocols = dyldCache->objcOpt()->protocolopt2();
777 if ( protocols == nullptr ) {
778 fprintf(stderr, "Error: could not get optimized objc protocols\n");
779 return 1;
780 }
781
782 for (uint64_t index = 0; index != protocols->capacity; ++index) {
783 const objc_opt::objc_classheader_t& clshi = protocols->classOffsets()[index];
784 if ( clshi.clsOffset == 0 ) {
785 fprintf(stderr, "[% 5lld]\n", index);
786 continue;
787 }
788 const char* name = (const char*)(((const uint8_t*)protocols) + protocols->offsets()[index]);
789 if ( !clshi.isDuplicate() ) {
790 fprintf(stderr, "[% 5lld] -> (% 8d, % 8d) = %s\n", index, clshi.clsOffset, clshi.hiOffset, name);
791 continue;
792 }
793
794 // class appears in more than one header
795 uint32_t count = clshi.duplicateCount();
796 fprintf(stderr, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n",
797 index, clshi.duplicateIndex(), clshi.duplicateIndex() + clshi.duplicateCount() - 1, name);
798
799 const objc_opt::objc_classheader_t *list = &protocols->duplicateOffsets()[clshi.duplicateIndex()];
800 for (uint32_t i = 0; i < count; i++) {
801 fprintf(stderr, " - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi.duplicateIndex() + i), list[i].clsOffset, list[i].hiOffset);
802 }
803 }
804 }
805 else if ( options.mode == modeObjCClasses ) {
806 using dyld3::json::Node;
807 using dyld3::json::NodeValueType;
808 using ObjCClassInfo = dyld3::MachOAnalyzer::ObjCClassInfo;
809 const bool rebased = false;
810
811 std::string instancePrefix("-");
812 std::string classPrefix("+");
813
814 auto getString = ^const char *(const dyld3::MachOAnalyzer* ma, uint64_t nameVMAddr){
815 dyld3::MachOAnalyzer::PrintableStringResult result;
816 const char* name = ma->getPrintableString(nameVMAddr, result);
817 if (result == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
818 return name;
819 return nullptr;
820 };
821
822 // Build a map of class vm addrs to their names so that categories know the
823 // name of the class they are attaching to
824 __block std::unordered_map<uint64_t, const char*> classVMAddrToName;
825 __block std::unordered_map<uint64_t, const char*> metaclassVMAddrToName;
826 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
827 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
828 const uint32_t pointerSize = ma->pointerSize();
829
830 auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
831 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
832 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
833 if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) {
834 if (isMetaClass)
835 metaclassVMAddrToName[classVMAddr] = className;
836 else
837 classVMAddrToName[classVMAddr] = className;
838 }
839 };
840
841 Diagnostics diag;
842
843 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(rebased);
844 ma->forEachObjCClass(diag, vmAddrConverter, visitClass);
845 });
846
847 // These are used only for the on-disk binaries we analyze
848 __block std::vector<const char*> onDiskChainedFixupBindTargets;
849 __block std::unordered_map<uint64_t, const char*> onDiskClassVMAddrToName;
850 __block std::unordered_map<uint64_t, const char*> onDiskMetaclassVMAddrToName;
851
852 auto getProperties = ^(const dyld3::MachOAnalyzer* ma, uint64_t propertiesVMAddr,
853 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
854 __block Node propertiesNode;
855 auto visitProperty = ^(uint64_t propertyVMAddr, const dyld3::MachOAnalyzer::ObjCProperty& property) {
856 // Get the name && attributes
857 auto propertyName = getString(ma, property.nameVMAddr);
858 auto propertyAttributes = getString(ma, property.attributesVMAddr);
859
860 if (!propertyName || !propertyAttributes)
861 return;
862
863 Node propertyNode;
864 propertyNode.map["name"] = Node{propertyName};
865 propertyNode.map["attributes"] = Node{propertyAttributes};
866 propertiesNode.array.push_back(propertyNode);
867 };
868 ma->forEachObjCProperty(propertiesVMAddr, vmAddrConverter, visitProperty);
869 return propertiesNode.array.empty() ? std::optional<Node>() : propertiesNode;
870 };
871
872 auto getClassProtocols = ^(const dyld3::MachOAnalyzer* ma, uint64_t protocolsVMAddr,
873 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
874 __block Node protocolsNode;
875
876 auto visitProtocol = ^(uint64_t protocolVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol& protocol) {
877 if (const char *name = getString(ma, protocol.nameVMAddr)) {
878 protocolsNode.array.push_back(Node{name});
879 }
880 };
881
882 ma->forEachObjCProtocol(protocolsVMAddr, vmAddrConverter, visitProtocol);
883
884 return protocolsNode.array.empty() ? std::optional<Node>() : protocolsNode;
885 };
886
887 auto getProtocols = ^(const dyld3::MachOAnalyzer* ma,
888 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
889 __block Node protocols;
890
891 auto getMethods = ^(const dyld3::MachOAnalyzer* ma, uint64_t methodListVMAddr, const std::string &prefix, Node &node){
892 auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
893 if (auto name = getString(ma, method.nameVMAddr)) {
894 node.array.push_back(Node{prefix + name});
895 }
896 };
897
898 ma->forEachObjCMethod(methodListVMAddr, vmAddrConverter, visitMethod);
899 };
900
901 auto visitProtocol = ^(Diagnostics& diag, uint64_t protoVMAddr,
902 const dyld3::MachOAnalyzer::ObjCProtocol& objcProto) {
903 const char* protoName = getString(ma, objcProto.nameVMAddr);
904 if (!protoName)
905 return;
906
907 Node entry;
908 entry.map["protocolName"] = Node{protoName};
909
910 if ( objcProto.protocolsVMAddr != 0 ) {
911 __block Node protocols;
912
913 auto visitProtocol = ^(uint64_t protocolRefVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol &protocol) {
914 if (auto name = getString(ma, protocol.nameVMAddr)) {
915 protocols.array.push_back(Node{name});
916 }
917 };
918
919 ma->forEachObjCProtocol(objcProto.protocolsVMAddr, vmAddrConverter, visitProtocol);
920 if (!protocols.array.empty()) {
921 entry.map["protocols"] = protocols;
922 }
923 }
924
925 Node methods;
926 getMethods(ma, objcProto.instanceMethodsVMAddr, instancePrefix, methods);
927 getMethods(ma, objcProto.classMethodsVMAddr, classPrefix, methods);
928 if (!methods.array.empty()) {
929 entry.map["methods"] = methods;
930 }
931
932 Node optMethods;
933 getMethods(ma, objcProto.optionalInstanceMethodsVMAddr, instancePrefix, optMethods);
934 getMethods(ma, objcProto.optionalClassMethodsVMAddr, classPrefix, optMethods);
935 if (!optMethods.array.empty()) {
936 entry.map["optionalMethods"] = optMethods;
937 }
938
939 protocols.array.push_back(entry);
940 };
941
942 Diagnostics diag;
943 ma->forEachObjCProtocol(diag, vmAddrConverter, visitProtocol);
944
945 return protocols.array.empty() ? std::optional<Node>() : protocols;
946 };
947
948 auto getSelRefs = ^(const dyld3::MachOAnalyzer* ma,
949 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
950 __block std::vector<const char *> selNames;
951
952 auto visitSelRef = ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr) {
953 if (auto selValue = getString(ma, selRefTargetVMAddr)) {
954 selNames.push_back(selValue);
955 }
956 };
957
958 Diagnostics diag;
959 ma->forEachObjCSelectorReference(diag, vmAddrConverter, visitSelRef);
960
961 std::sort(selNames.begin(), selNames.end(),
962 [](const char* a, const char* b) {
963 return strcasecmp(a, b) < 0;
964 });
965
966 Node selrefs;
967 for (auto s: selNames) {
968 selrefs.array.push_back(Node{s});
969 }
970
971 return selrefs.array.empty() ? std::optional<Node>() : selrefs;
972 };
973
974 auto getClasses = ^(const dyld3::MachOAnalyzer* ma,
975 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
976 Diagnostics diag;
977 const uint32_t pointerSize = ma->pointerSize();
978
979 // Get the vmAddrs for all exported symbols as we want to know if classes
980 // are exported
981 std::set<uint64_t> exportedSymbolVMAddrs;
982 {
983 uint64_t loadAddress = ma->preferredLoadAddress();
984
985 uint32_t exportTrieRuntimeOffset;
986 uint32_t exportTrieSize;
987 if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) {
988 const uint8_t* start = (uint8_t*)ma + exportTrieRuntimeOffset;
989 const uint8_t* end = start + exportTrieSize;
990 std::vector<ExportInfoTrie::Entry> exports;
991 if ( ExportInfoTrie::parseTrie(start, end, exports) ) {
992 for (const ExportInfoTrie::Entry& entry: exports) {
993 exportedSymbolVMAddrs.insert(loadAddress + entry.info.address);
994 }
995 }
996 }
997 }
998
999 __block Node classesNode;
1000 __block bool skippedPreviousClass = false;
1001 auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
1002 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
1003 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
1004 if (isMetaClass) {
1005 if (skippedPreviousClass) {
1006 // If the class was bad, then skip the meta class too
1007 skippedPreviousClass = false;
1008 return;
1009 }
1010 } else {
1011 skippedPreviousClass = true;
1012 }
1013
1014 std::string classType = "-";
1015 if (isMetaClass)
1016 classType = "+";
1017 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
1018 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
1019 if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) {
1020 return;
1021 }
1022
1023 const char* superClassName = nullptr;
1024 if ( ma->inDyldCache() ) {
1025 if ( objcClass.superclassVMAddr != 0 ) {
1026 if (isMetaClass) {
1027 // If we are root class, then our superclass should actually point to our own class
1028 const uint32_t RO_ROOT = (1<<1);
1029 if ( objcClass.flags(pointerSize) & RO_ROOT ) {
1030 auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
1031 assert(it != classVMAddrToName.end());
1032 superClassName = it->second;
1033 } else {
1034 auto it = metaclassVMAddrToName.find(objcClass.superclassVMAddr);
1035 assert(it != metaclassVMAddrToName.end());
1036 superClassName = it->second;
1037 }
1038 } else {
1039 auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
1040 assert(it != classVMAddrToName.end());
1041 superClassName = it->second;
1042 }
1043 }
1044 } else {
1045 // On-disk binary. Lets crack the chain to work out what we are pointing at
1046 dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
1047 fixup.raw64 = objcClass.superclassVMAddr;
1048 if (fixup.arm64e.bind.bind) {
1049 uint64_t bindOrdinal = fixup.arm64e.authBind.auth ? fixup.arm64e.authBind.ordinal : fixup.arm64e.bind.ordinal;
1050 // Bind to another image. Use the bind table to work out which name to bind to
1051 const char* symbolName = onDiskChainedFixupBindTargets[bindOrdinal];
1052 if (isMetaClass) {
1053 if ( strstr(symbolName, "_OBJC_METACLASS_$_") == symbolName ) {
1054 superClassName = symbolName + strlen("_OBJC_METACLASS_$_");
1055 } else {
1056 // Swift classes don't start with these prefixes so just skip them
1057 if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
1058 return;
1059 }
1060 } else {
1061 if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
1062 superClassName = symbolName + strlen("_OBJC_CLASS_$_");
1063 } else {
1064 // Swift classes don't start with these prefixes so just skip them
1065 if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
1066 return;
1067 }
1068 }
1069 } else {
1070 // Rebase within this image.
1071 if (isMetaClass) {
1072 auto it = onDiskMetaclassVMAddrToName.find(objcClass.superclassVMAddr);
1073 assert(it != onDiskMetaclassVMAddrToName.end());
1074 superClassName = it->second;
1075 } else {
1076 auto it = onDiskClassVMAddrToName.find(objcClass.superclassVMAddr);
1077 assert(it != onDiskClassVMAddrToName.end());
1078 superClassName = it->second;
1079 }
1080 }
1081 }
1082
1083 // Print the methods on this class
1084 __block Node methodsNode;
1085 auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1086 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
1087 const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
1088 if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
1089 return;
1090 methodsNode.array.push_back(Node{classType + methodName});
1091 };
1092 ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter,
1093 visitMethod);
1094
1095 std::optional<Node> properties = getProperties(ma, objcClass.basePropertiesVMAddr(pointerSize), vmAddrConverter);
1096
1097 if (isMetaClass) {
1098 assert(!classesNode.array.empty());
1099 Node& currentClassNode = classesNode.array.back();
1100 assert(currentClassNode.map["className"].value == className);
1101 if (!methodsNode.array.empty()) {
1102 Node& currentMethodsNode = currentClassNode.map["methods"];
1103 currentMethodsNode.array.insert(currentMethodsNode.array.end(),
1104 methodsNode.array.begin(),
1105 methodsNode.array.end());
1106 }
1107 if (properties.has_value()) {
1108 Node& currentPropertiesNode = currentClassNode.map["properties"];
1109 currentPropertiesNode.array.insert(currentPropertiesNode.array.end(),
1110 properties->array.begin(),
1111 properties->array.end());
1112 }
1113 return;
1114 }
1115
1116 Node currentClassNode;
1117 currentClassNode.map["className"] = Node{className};
1118 if ( superClassName != nullptr )
1119 currentClassNode.map["superClassName"] = Node{superClassName};
1120 if (!methodsNode.array.empty())
1121 currentClassNode.map["methods"] = methodsNode;
1122 if (properties.has_value())
1123 currentClassNode.map["properties"] = properties.value();
1124 if (std::optional<Node> protocols = getClassProtocols(ma, objcClass.baseProtocolsVMAddr(pointerSize), vmAddrConverter))
1125 currentClassNode.map["protocols"] = protocols.value();
1126
1127 currentClassNode.map["exported"] = Node{exportedSymbolVMAddrs.count(classVMAddr) != 0};
1128
1129 // We didn't skip this class so mark it as such
1130 skippedPreviousClass = false;
1131
1132 classesNode.array.push_back(currentClassNode);
1133 };
1134
1135 ma->forEachObjCClass(diag, vmAddrConverter, visitClass);
1136 return classesNode.array.empty() ? std::optional<Node>() : classesNode;
1137 };
1138
1139 auto getCategories = ^(const dyld3::MachOAnalyzer* ma,
1140 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
1141 Diagnostics diag;
1142
1143 __block Node categoriesNode;
1144 auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
1145 const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
1146 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
1147 const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
1148 if (categoryNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
1149 return;
1150
1151 const char* className = nullptr;
1152 if ( ma->inDyldCache() ) {
1153 auto it = classVMAddrToName.find(objcCategory.clsVMAddr);
1154 assert(it != classVMAddrToName.end());
1155 className = it->second;
1156 } else {
1157 // On-disk binary. Lets crack the chain to work out what we are pointing at
1158 dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
1159 fixup.raw64 = objcCategory.clsVMAddr;
1160 if (fixup.arm64e.bind.bind) {
1161 uint64_t bindOrdinal = fixup.arm64e.authBind.auth ? fixup.arm64e.authBind.ordinal : fixup.arm64e.bind.ordinal;
1162 // Bind to another image. Use the bind table to work out which name to bind to
1163 const char* symbolName = onDiskChainedFixupBindTargets[bindOrdinal];
1164 if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
1165 className = symbolName + strlen("_OBJC_CLASS_$_");
1166 } else {
1167 // Swift classes don't start with these prefixes so just skip them
1168 // We don't know that this is a Swift class/category though, but skip it anyway
1169 return;
1170 }
1171 } else {
1172 auto it = onDiskClassVMAddrToName.find(objcCategory.clsVMAddr);
1173 if (it == onDiskClassVMAddrToName.end()) {
1174 // This is an odd binary with perhaps a Swift class. Just skip this entry
1175 return;
1176 }
1177 className = it->second;
1178 }
1179 }
1180
1181 // Print the instance methods on this category
1182 __block Node methodsNode;
1183 auto visitInstanceMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1184 if (auto methodName = getString(ma, method.nameVMAddr))
1185 methodsNode.array.push_back(Node{instancePrefix + methodName});
1186 };
1187 ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter,
1188 visitInstanceMethod);
1189
1190 // Print the instance methods on this category
1191 __block Node classMethodsNode;
1192 auto visitClassMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1193 if (auto methodName = getString(ma, method.nameVMAddr))
1194 methodsNode.array.push_back(Node{classPrefix + methodName});
1195 };
1196 ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter,
1197 visitClassMethod);
1198
1199 Node currentCategoryNode;
1200 currentCategoryNode.map["categoryName"] = Node{categoryName};
1201 currentCategoryNode.map["className"] = Node{className};
1202 if (!methodsNode.array.empty())
1203 currentCategoryNode.map["methods"] = methodsNode;
1204 if (std::optional<Node> properties = getProperties(ma, objcCategory.instancePropertiesVMAddr, vmAddrConverter))
1205 currentCategoryNode.map["properties"] = properties.value();
1206 if (std::optional<Node> protocols = getClassProtocols(ma, objcCategory.protocolsVMAddr, vmAddrConverter))
1207 currentCategoryNode.map["protocols"] = protocols.value();
1208
1209 categoriesNode.array.push_back(currentCategoryNode);
1210 };
1211
1212 ma->forEachObjCCategory(diag, vmAddrConverter, visitCategory);
1213 return categoriesNode.array.empty() ? std::optional<Node>() : categoriesNode;
1214 };
1215
1216 __block bool needsComma = false;
1217
1218 dyld3::json::streamArrayBegin(needsComma);
1219
1220 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
1221 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(rebased);
1222 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
1223
1224 Node imageRecord;
1225 imageRecord.map["imagePath"] = Node{installName};
1226 imageRecord.map["imageType"] = Node{"cache-dylib"};
1227 std::optional<Node> classes = getClasses(ma, vmAddrConverter);
1228 std::optional<Node> categories = getCategories(ma, vmAddrConverter);
1229 std::optional<Node> protocols = getProtocols(ma, vmAddrConverter);
1230 std::optional<Node> selrefs = getSelRefs(ma, vmAddrConverter);
1231
1232 // Skip emitting images with no objc data
1233 if (!classes.has_value() && !categories.has_value() && !protocols.has_value() && !selrefs.has_value())
1234 return;
1235 if (classes.has_value())
1236 imageRecord.map["classes"] = classes.value();
1237 if (categories.has_value())
1238 imageRecord.map["categories"] = categories.value();
1239 if (protocols.has_value())
1240 imageRecord.map["protocols"] = protocols.value();
1241 if (selrefs.has_value())
1242 imageRecord.map["selrefs"] = selrefs.value();
1243
1244 dyld3::json::streamArrayNode(needsComma, imageRecord);
1245 });
1246
1247 FileSystemPhysical fileSystem;
1248 dyld3::Platform platform = dyldCache->platform();
1249 const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(dyldCache->archName(), true);
1250
1251 dyldCache->forEachLaunchClosure(^(const char *executableRuntimePath, const dyld3::closure::LaunchClosure *closure) {
1252 Diagnostics diag;
1253 char realerPath[MAXPATHLEN];
1254 dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, executableRuntimePath, archs, platform, realerPath);
1255 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
1256 uint32_t pointerSize = ma->pointerSize();
1257
1258 // Populate the bind targets for classes from other images
1259 onDiskChainedFixupBindTargets.clear();
1260 ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
1261 onDiskChainedFixupBindTargets.push_back(symbolName);
1262 });
1263 if ( diag.hasError() )
1264 return;
1265
1266 // Populate the rebase targets for class names
1267 onDiskMetaclassVMAddrToName.clear();
1268 onDiskClassVMAddrToName.clear();
1269 auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
1270 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
1271 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
1272 if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) {
1273 if (isMetaClass)
1274 onDiskMetaclassVMAddrToName[classVMAddr] = className;
1275 else
1276 onDiskClassVMAddrToName[classVMAddr] = className;
1277 }
1278 };
1279
1280 // Get a vmAddrConverter for this on-disk binary. We can't use the shared cache one
1281 dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter = ma->makeVMAddrConverter(rebased);
1282
1283 ma->forEachObjCClass(diag, onDiskVMAddrConverter, visitClass);
1284
1285 Node imageRecord;
1286 imageRecord.map["imagePath"] = Node{executableRuntimePath};
1287 imageRecord.map["imageType"] = Node{"executable"};
1288 std::optional<Node> classes = getClasses(ma, onDiskVMAddrConverter);
1289 std::optional<Node> categories = getCategories(ma, onDiskVMAddrConverter);
1290 // TODO: protocols
1291 std::optional<Node> selrefs = getSelRefs(ma, onDiskVMAddrConverter);
1292
1293 // Skip emitting images with no objc data
1294 if (!classes.has_value() && !categories.has_value() && !selrefs.has_value())
1295 return;
1296 if (classes.has_value())
1297 imageRecord.map["classes"] = classes.value();
1298 if (categories.has_value())
1299 imageRecord.map["categories"] = categories.value();
1300 if (selrefs.has_value())
1301 imageRecord.map["selrefs"] = selrefs.value();
1302
1303 dyld3::json::streamArrayNode(needsComma, imageRecord);
1304 });
1305
1306 dyldCache->forEachDlopenImage(^(const char *runtimePath, const dyld3::closure::Image *image) {
1307 Diagnostics diag;
1308 char realerPath[MAXPATHLEN];
1309 dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath, archs, platform, realerPath);
1310 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
1311 uint32_t pointerSize = ma->pointerSize();
1312
1313 // Populate the bind targets for classes from other images
1314 onDiskChainedFixupBindTargets.clear();
1315 ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
1316 onDiskChainedFixupBindTargets.push_back(symbolName);
1317 });
1318 if ( diag.hasError() )
1319 return;
1320
1321 // Populate the rebase targets for class names
1322 onDiskMetaclassVMAddrToName.clear();
1323 onDiskClassVMAddrToName.clear();
1324 auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
1325 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
1326 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
1327 if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) {
1328 if (isMetaClass)
1329 onDiskMetaclassVMAddrToName[classVMAddr] = className;
1330 else
1331 onDiskClassVMAddrToName[classVMAddr] = className;
1332 }
1333 };
1334
1335 // Get a vmAddrConverter for this on-disk binary. We can't use the shared cache one
1336 dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter = ma->makeVMAddrConverter(rebased);
1337
1338 ma->forEachObjCClass(diag, onDiskVMAddrConverter, visitClass);
1339
1340 Node imageRecord;
1341 imageRecord.map["imagePath"] = Node{runtimePath};
1342 imageRecord.map["imageType"] = Node{"non-cache-dylib"};
1343 std::optional<Node> classes = getClasses(ma, onDiskVMAddrConverter);
1344 std::optional<Node> categories = getCategories(ma, onDiskVMAddrConverter);
1345 // TODO: protocols
1346 std::optional<Node> selrefs = getSelRefs(ma, onDiskVMAddrConverter);
1347
1348 // Skip emitting images with no objc data
1349 if (!classes.has_value() && !categories.has_value() && !selrefs.has_value())
1350 return;
1351 if (classes.has_value())
1352 imageRecord.map["classes"] = classes.value();
1353 if (categories.has_value())
1354 imageRecord.map["categories"] = categories.value();
1355 if (selrefs.has_value())
1356 imageRecord.map["selrefs"] = selrefs.value();
1357
1358 dyld3::json::streamArrayNode(needsComma, imageRecord);
1359 });
1360
1361 dyld3::json::streamArrayEnd(needsComma);
1362 }
1363 else if ( options.mode == modeObjCSelectors ) {
1364 if ( dyldCache->objcOpt() == nullptr ) {
1365 fprintf(stderr, "Error: could not get optimized objc\n");
1366 return 1;
1367 }
1368 const objc_opt::objc_selopt_t* selectors = dyldCache->objcOpt()->selopt();
1369 if ( selectors == nullptr ) {
1370 fprintf(stderr, "Error: could not get optimized objc selectors\n");
1371 return 1;
1372 }
1373
1374 std::vector<const char*> selNames;
1375 for (uint64_t index = 0; index != selectors->capacity; ++index) {
1376 objc_opt::objc_stringhash_offset_t offset = selectors->offsets()[index];
1377 if ( offset == 0 )
1378 continue;
1379 const char* selName = selectors->getEntryForIndex((uint32_t)index);
1380 selNames.push_back(selName);
1381 }
1382
1383 std::sort(selNames.begin(), selNames.end(),
1384 [](const char* a, const char* b) {
1385 // Sort by offset, not string value
1386 return a < b;
1387 });
1388
1389 dyld3::json::Node root;
1390 for (const char* selName : selNames) {
1391 dyld3::json::Node selNode;
1392 selNode.map["selectorName"] = dyld3::json::Node{selName};
1393 selNode.map["offset"] = dyld3::json::Node{(int64_t)selName - (int64_t)dyldCache};
1394
1395 root.array.push_back(selNode);
1396 }
1397
1398 dyld3::json::printJSON(root, 0, std::cout);
1399 }
1400 else if ( options.mode == modeExtract ) {
1401 return dyld_shared_cache_extract_dylibs(sharedCachePath, options.extractionDir);
1402 }
1403 else if ( options.mode == modeObjCImpCaches ) {
1404 if (sharedCachePath == nullptr) {
1405 fprintf(stderr, "Cannot emit imp caches with live cache. Run again with the path to the cache file\n");
1406 return 1;
1407 }
1408 __block std::map<uint64_t, const char*> methodToClassMap;
1409 __block std::map<uint64_t, const char*> classVMAddrToNameMap;
1410 const bool contentRebased = false;
1411 const uint32_t pointerSize = 8;
1412
1413 // Get the base pointers from the magic section in objc
1414 __block uint64_t objcCacheOffsetsSize = 0;
1415 __block const void* objcCacheOffsets = nullptr;
1416 __block Diagnostics diag;
1417 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1418 if ( !strcmp(installName, "/usr/lib/libobjc.A.dylib") ) {
1419 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
1420 objcCacheOffsets = ma->findSectionContent("__DATA_CONST", "__objc_scoffs", objcCacheOffsetsSize);
1421 }
1422 });
1423
1424 if ( objcCacheOffsets == nullptr ) {
1425 fprintf(stderr, "Unable to print imp-caches as cannot find __DATA_CONST __objc_scoffs inside /usr/lib/libobjc.A.dylib\n");
1426 return 1;
1427 }
1428
1429 if ( objcCacheOffsetsSize < (4 * pointerSize) ) {
1430 fprintf(stderr, "Unable to print imp-caches as __DATA_CONST __objc_scoffs is too small (%lld vs required %u)\n", objcCacheOffsetsSize, (4 * pointerSize));
1431 return 1;
1432 }
1433
1434 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(contentRebased);
1435
1436 uint64_t selectorStringVMAddrStart = vmAddrConverter.convertToVMAddr(((uint64_t*)objcCacheOffsets)[0]);
1437 uint64_t selectorStringVMAddrEnd = vmAddrConverter.convertToVMAddr(((uint64_t*)objcCacheOffsets)[1]);
1438
1439 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
1440 if (diag.hasError())
1441 return;
1442
1443 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
1444 intptr_t slide = ma->getSlide();
1445
1446 ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag,
1447 uint64_t classVMAddr,
1448 uint64_t classSuperclassVMAddr,
1449 uint64_t classDataVMAddr,
1450 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass,
1451 bool isMetaClass) {
1452 const char* className = (const char*)objcClass.nameVMAddr(pointerSize) + slide;
1453 classVMAddrToNameMap[classVMAddr] = className;
1454 ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter,
1455 ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1456 // const char* methodName = (const char*)(method.nameVMAddr + slide);
1457 methodToClassMap[method.impVMAddr] = className;
1458 });
1459 });
1460
1461 ma->forEachObjCCategory(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
1462 ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1463 const char* catName = (const char*)objcCategory.nameVMAddr + slide;
1464 // const char* methodName = (const char*)(method.nameVMAddr + slide);
1465 methodToClassMap[method.impVMAddr] = catName;
1466 });
1467
1468 ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1469 const char* catName = (const char*)objcCategory.nameVMAddr + slide;
1470 // const char* methodName = (const char*)(method.nameVMAddr + slide);
1471 methodToClassMap[method.impVMAddr] = catName;
1472 });
1473 });
1474 });
1475 if (diag.hasError())
1476 return 1;
1477
1478 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
1479 if (diag.hasError())
1480 return;
1481
1482 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
1483 intptr_t slide = ma->getSlide();
1484
1485 ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag,
1486 uint64_t classVMAddr,
1487 uint64_t classSuperclassVMAddr,
1488 uint64_t classDataVMAddr,
1489 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass,
1490 bool isMetaClass) {
1491 const char* type = "class";
1492 if (isMetaClass)
1493 type = "meta-class";
1494 const char* className = (const char*)objcClass.nameVMAddr(pointerSize) + slide;
1495
1496 if (objcClass.methodCacheVMAddr == 0) {
1497 printf("%s (%s): empty\n", className, type);
1498 return;
1499 }
1500
1501 struct Bucket {
1502 uint32_t selOffset;
1503 uint32_t impOffset;
1504 };
1505 struct ImpCache {
1506 int32_t fallback_class_offset;
1507 uint32_t cache_shift : 5;
1508 uint32_t cache_mask : 11;
1509 uint32_t occupied : 14;
1510 uint32_t has_inlines : 1;
1511 uint32_t bit_one : 1;
1512 struct Bucket buckets[];
1513 };
1514
1515 const ImpCache* impCache = (const ImpCache*)(objcClass.methodCacheVMAddr + slide);
1516 printf("%s (%s): %d buckets\n", className, type, impCache->cache_mask + 1);
1517
1518 if ((classVMAddr + impCache->fallback_class_offset) != objcClass.superclassVMAddr) {
1519 printf("Flattening fallback: %s\n", classVMAddrToNameMap[classVMAddr + impCache->fallback_class_offset]);
1520 }
1521 // Buckets are a 32-bit offset from the impcache itself
1522 for (uint32_t i = 0; i <= impCache->cache_mask ; ++i) {
1523 const Bucket& b = impCache->buckets[i];
1524 uint64_t sel = (uint64_t)b.selOffset + selectorStringVMAddrStart;
1525 uint64_t imp = classVMAddr - (uint64_t)b.impOffset;
1526 if (b.selOffset == 0xFFFFFFFF) {
1527 // Empty bucket
1528 printf(" - 0x%016llx: %s\n", 0ULL, "");
1529 } else {
1530 assert(sel < selectorStringVMAddrEnd);
1531
1532 auto it = methodToClassMap.find(imp);
1533 if (it == methodToClassMap.end()) {
1534 fprintf(stderr, "Could not find IMP %llx (for %s)\n", imp, (const char*)(sel + slide));
1535 }
1536 assert(it != methodToClassMap.end());
1537 printf(" - 0x%016llx: %s (from %s)\n", imp, (const char*)(sel + slide), it->second);
1538 }
1539 }
1540 });
1541 });
1542 } else {
1543 switch ( options.mode ) {
1544 case modeList: {
1545 // list all dylibs, including their aliases (symlinks to them) with option vmaddr
1546 __block std::vector<std::unordered_set<std::string>> indexToPaths;
1547 __block std::vector<uint64_t> indexToAddr;
1548 __block std::vector<std::string> indexToUUID;
1549 dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char* dylibUUID, const char* installName, bool& stop) {
1550 std::unordered_set<std::string> empty;
1551 if ( options.printVMAddrs )
1552 indexToAddr.push_back(loadAddressUnslid);
1553 if ( options.printUUIDs ) {
1554 uuid_string_t uuidString;
1555 uuid_unparse_upper(dylibUUID, uuidString);
1556 indexToUUID.push_back(uuidString);
1557 }
1558 indexToPaths.push_back(empty);
1559 indexToPaths.back().insert(installName);
1560 });
1561 dyldCache->forEachDylibPath(^(const char* dylibPath, uint32_t index) {
1562 indexToPaths[index].insert(dylibPath);
1563 });
1564 int index = 0;
1565 for (const std::unordered_set<std::string>& paths : indexToPaths) {
1566 for (const std::string& path: paths) {
1567 if ( options.printVMAddrs )
1568 printf("0x%08llX ", indexToAddr[index]);
1569 if ( options.printUUIDs )
1570 printf("<%s> ", indexToUUID[index].c_str());
1571 printf("%s\n", path.c_str());
1572 }
1573 ++index;
1574 }
1575 break;
1576 }
1577 case modeListDylibsWithSection: {
1578 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1579 dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
1580 mf->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
1581 if ( (strcmp(sectInfo.sectName, options.sectionName) == 0) && (strcmp(sectInfo.segInfo.segName, options.segmentName) == 0) ) {
1582 printf("%s\n", installName);
1583 stop = true;
1584 }
1585 });
1586 });
1587 break;
1588 }
1589 case modeMap: {
1590 __block std::map<uint64_t, const char*> dataSegNames;
1591 __block std::map<uint64_t, uint64_t> dataSegEnds;
1592 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1593 dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
1594 mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
1595 printf("0x%08llX - 0x%08llX %s %s\n", info.vmAddr, info.vmAddr + info.vmSize, info.segName, installName);
1596 if ( strncmp(info.segName, "__DATA", 6) == 0 ) {
1597 dataSegNames[info.vmAddr] = installName;
1598 dataSegEnds[info.vmAddr] = info.vmAddr + info.vmSize;
1599 }
1600 });
1601 });
1602 // <rdar://problem/51084507> Enhance dyld_shared_cache_util to show where section alignment added padding
1603 uint64_t lastEnd = 0;
1604 for (const auto& entry : dataSegEnds) {
1605 uint64_t padding = entry.first - lastEnd;
1606 if ( (padding > 32) && (lastEnd != 0) ) {
1607 printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd, entry.first, padding/1024);
1608 }
1609 lastEnd = entry.second;
1610 }
1611 break;
1612 }
1613 case modeDependencies: {
1614 __block bool dependentTargetFound = false;
1615 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1616 if ( strcmp(options.dependentsOfPath, installName) != 0 )
1617 return;
1618 dependentTargetFound = true;
1619
1620 auto printDep = [&options](const char *loadPath, uint32_t compatVersion, uint32_t curVersion) {
1621 if ( options.printDylibVersions ) {
1622 uint32_t compat_vers = compatVersion;
1623 uint32_t current_vers = curVersion;
1624 printf("\t%s", loadPath);
1625 if ( compat_vers != 0xFFFFFFFF ) {
1626 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
1627 (compat_vers >> 16),
1628 (compat_vers >> 8) & 0xff,
1629 (compat_vers) & 0xff,
1630 (current_vers >> 16),
1631 (current_vers >> 8) & 0xff,
1632 (current_vers) & 0xff);
1633 }
1634 else {
1635 printf("\n");
1636 }
1637 }
1638 else {
1639 printf("\t%s\n", loadPath);
1640 }
1641 };
1642
1643 dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
1644
1645 // First print out our dylib and version.
1646 const char* dylibInstallName;
1647 uint32_t currentVersion;
1648 uint32_t compatVersion;
1649 if ( mf->getDylibInstallName(&dylibInstallName, &compatVersion, &currentVersion) ) {
1650 printDep(dylibInstallName, compatVersion, currentVersion);
1651 }
1652
1653 // Then the dependent dylibs.
1654 mf->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
1655 printDep(loadPath, compatVersion, curVersion);
1656 });
1657 });
1658 if (options.dependentsOfPath && !dependentTargetFound) {
1659 fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath);
1660 exit(1);
1661 }
1662 break;
1663 }
1664 case modeLinkEdit: {
1665 std::map<uint32_t, const char*> pageToContent;
1666 auto add_linkedit = [&pageToContent](uint32_t pageStart, uint32_t pageEnd, const char* message) {
1667 for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
1668 std::map<uint32_t, const char*>::iterator pos = pageToContent.find(p);
1669 if ( pos == pageToContent.end() ) {
1670 pageToContent[p] = strdup(message);
1671 }
1672 else {
1673 const char* oldMessage = pos->second;
1674 char* newMesssage;
1675 asprintf(&newMesssage, "%s, %s", oldMessage, message);
1676 pageToContent[p] = newMesssage;
1677 ::free((void*)oldMessage);
1678 }
1679 }
1680 };
1681
1682 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1683 dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1684 Diagnostics diag;
1685 dyld3::MachOAnalyzer::LinkEditInfo leInfo;
1686 ma->getLinkEditPointers(diag, leInfo);
1687
1688 if (diag.hasError())
1689 return;
1690
1691 char message[1000];
1692 const char* shortName = strrchr(installName, '/') + 1;
1693 // add export trie info
1694 if ( leInfo.dyldInfo->export_size != 0 ) {
1695 //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off());
1696 uint32_t exportPageOffsetStart = leInfo.dyldInfo->export_off & (-4096);
1697 uint32_t exportPageOffsetEnd = (leInfo.dyldInfo->export_off + leInfo.dyldInfo->export_size) & (-4096);
1698 sprintf(message, "exports from %s", shortName);
1699 add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message);
1700 }
1701 // add binding info
1702 if ( leInfo.dyldInfo->bind_size != 0 ) {
1703 uint32_t bindPageOffsetStart = leInfo.dyldInfo->bind_off & (-4096);
1704 uint32_t bindPageOffsetEnd = (leInfo.dyldInfo->bind_off + leInfo.dyldInfo->bind_size) & (-4096);
1705 sprintf(message, "bindings from %s", shortName);
1706 add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message);
1707 }
1708 // add lazy binding info
1709 if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
1710 uint32_t lazybindPageOffsetStart = leInfo.dyldInfo->lazy_bind_off & (-4096);
1711 uint32_t lazybindPageOffsetEnd = (leInfo.dyldInfo->lazy_bind_off + leInfo.dyldInfo->lazy_bind_size) & (-4096);
1712 sprintf(message, "lazy bindings from %s", shortName);
1713 add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message);
1714 }
1715 // add weak binding info
1716 if ( leInfo.dyldInfo->weak_bind_size != 0 ) {
1717 uint32_t weakbindPageOffsetStart = leInfo.dyldInfo->weak_bind_off & (-4096);
1718 uint32_t weakbindPageOffsetEnd = (leInfo.dyldInfo->weak_bind_off + leInfo.dyldInfo->weak_bind_size) & (-4096);
1719 sprintf(message, "weak bindings from %s", shortName);
1720 add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message);
1721 }
1722 });
1723
1724 for (std::map<uint32_t, const char*>::iterator it = pageToContent.begin(); it != pageToContent.end(); ++it) {
1725 printf("0x%08X %s\n", it->first, it->second);
1726 }
1727 break;
1728 }
1729 case modeSize: {
1730 struct TextInfo {
1731 uint64_t textSize;
1732 const char* path;
1733 };
1734 __block std::vector<TextInfo> textSegments;
1735 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1736
1737 dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1738 ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
1739 if ( strcmp(info.segName, "__TEXT") != 0 )
1740 return;
1741 textSegments.push_back({ info.fileSize, installName });
1742 });
1743 });
1744 std::sort(textSegments.begin(), textSegments.end(), [](const TextInfo& left, const TextInfo& right) {
1745 return (left.textSize > right.textSize);
1746 });
1747 for (std::vector<TextInfo>::iterator it = textSegments.begin(); it != textSegments.end(); ++it) {
1748 printf(" 0x%08llX %s\n", it->textSize, it->path);
1749 }
1750 break;
1751 }
1752 case modePatchTable: {
1753 std::vector<SegmentInfo> segInfos;
1754 buildSegmentInfo(dyldCache, segInfos);
1755 __block uint32_t imageIndex = 0;
1756 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1757 printf("%s:\n", installName);
1758 dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* exportName) {
1759 printf(" export: 0x%08X %s\n", cacheOffsetOfImpl, exportName);
1760 dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) {
1761 SegmentInfo usageAt;
1762 const uint64_t patchLocVmAddr = dyldCache->unslidLoadAddress() + patchLocation.cacheOffset;
1763 findImageAndSegment(dyldCache, segInfos, patchLocation.cacheOffset, &usageAt);
1764 if ( patchLocation.addend == 0 )
1765 printf(" used by: %s+0x%04llX in %s\n", usageAt.segName, patchLocVmAddr-usageAt.vmAddr, usageAt.installName);
1766 else
1767 printf(" used by: %s+0x%04llX (addend=%d) in %s\n", usageAt.segName, patchLocVmAddr-usageAt.vmAddr, patchLocation.addend, usageAt.installName);
1768 });
1769 });
1770 ++imageIndex;
1771 });
1772 break;
1773 }
1774 case modeNone:
1775 case modeInfo:
1776 case modeSlideInfo:
1777 case modeVerboseSlideInfo:
1778 case modeTextInfo:
1779 case modeLocalSymbols:
1780 case modeJSONMap:
1781 case modeJSONDependents:
1782 case modeSectionSizes:
1783 case modeStrings:
1784 case modeObjCProtocols:
1785 case modeObjCImpCaches:
1786 case modeObjCClasses:
1787 case modeObjCSelectors:
1788 case modeExtract:
1789 break;
1790 }
1791 }
1792 return 0;
1793 }