dyld-832.7.1.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 = 32 - __builtin_clz(possibleSlideValues - 1);
615 printf("ASLR entropy: %u-bits\n", entropyBits);
616 }
617 printf("mappings:\n");
618 dyldCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions,
619 uint64_t flags) {
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";
627 else
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";
634 }
635 else if ( permissions & VM_PROT_READ ) {
636 mappingName = "__LINKEDIT";
637 } else {
638 mappingName = "*unknown*";
639 }
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);
643 });
644 if ( header->codeSignatureOffset != 0 ) {
645 uint64_t size = header->codeSignatureSize;
646 uint64_t csAddr = dyldCache->getCodeSignAddress();
647 if ( size != 0 )
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);
650 }
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) {
653
654 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
655 slideInfoSize/1024, slideInfoOffset, slideInfoOffset + slideInfoSize);
656 });
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);
660 }
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);
668 });
669 }
670 else if ( options.mode == modeLocalSymbols ) {
671 if ( !dyldCache->hasLocalSymbolsInfo() ) {
672 fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
673 exit(1);
674 }
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();
682
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);
685
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);
690 #if 0
691 if ( is64 ) {
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);
697 }
698 }
699 #endif
700 entriesCount++;
701 });
702 printf("local symbols by dylib (count=%d):\n", entriesCount);
703 }
704 else if ( options.mode == modeJSONMap ) {
705 std::string buffer = dyldCache->generateJSONMap("unknown");
706 printf("%s\n", buffer.c_str());
707 }
708 else if ( options.mode == modeJSONDependents ) {
709 std::cout << dyldCache->generateJSONDependents();
710 }
711 else if ( options.mode == modeStrings ) {
712 if (printStrings) {
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 ) {
719 stop = true;
720 return;
721 }
722 const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
723 const char* s = (char*)content;
724 const char* end = s + info.sectSize;
725 while ( s < end ) {
726 printf("%s: %s\n", ma->installName(), s);
727 while (*s != '\0' )
728 ++s;
729 ++s;
730 }
731 }
732 });
733 });
734 }
735
736 if (printExports) {
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) ) {
746 return;
747 }
748
749 for (const ExportInfoTrie::Entry& entry: exports) {
750 printf("%s: %s\n", ma->installName(), entry.name.c_str());
751 }
752 }
753 });
754 }
755 }
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 &sectInfo, bool malformedSectionRange, bool &stop) {
761 std::string section = std::string(sectInfo.segInfo.segName) + " " + sectInfo.sectName;
762 sectionSizes[section] += sectInfo.sectSize;
763 });
764 });
765 for (const auto& keyAndValue : sectionSizes) {
766 printf("%lld %s\n", keyAndValue.second, keyAndValue.first.c_str());
767 }
768 }
769 else if ( options.mode == modeObjCProtocols ) {
770 if ( dyldCache->objcOpt() == nullptr ) {
771 fprintf(stderr, "Error: could not get optimized objc\n");
772 return 1;
773 }
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");
777 return 1;
778 }
779
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);
784 continue;
785 }
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);
789 continue;
790 }
791
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);
796
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);
800 }
801 }
802 }
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;
808
809 std::string instancePrefix("-");
810 std::string classPrefix("+");
811
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)
816 return name;
817 return nullptr;
818 };
819
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();
827
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))) {
832 if (isMetaClass)
833 metaclassVMAddrToName[classVMAddr] = className;
834 else
835 classVMAddrToName[classVMAddr] = className;
836 }
837 };
838
839 Diagnostics diag;
840
841 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(rebased);
842 ma->forEachObjCClass(diag, vmAddrConverter, visitClass);
843 });
844
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;
849
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);
857
858 if (!propertyName || !propertyAttributes)
859 return;
860
861 Node propertyNode;
862 propertyNode.map["name"] = Node{propertyName};
863 propertyNode.map["attributes"] = Node{propertyAttributes};
864 propertiesNode.array.push_back(propertyNode);
865 };
866 ma->forEachObjCProperty(propertiesVMAddr, vmAddrConverter, visitProperty);
867 return propertiesNode.array.empty() ? std::optional<Node>() : propertiesNode;
868 };
869
870 auto getClassProtocols = ^(const dyld3::MachOAnalyzer* ma, uint64_t protocolsVMAddr,
871 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
872 __block Node protocolsNode;
873
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});
877 }
878 };
879
880 ma->forEachObjCProtocol(protocolsVMAddr, vmAddrConverter, visitProtocol);
881
882 return protocolsNode.array.empty() ? std::optional<Node>() : protocolsNode;
883 };
884
885 auto getProtocols = ^(const dyld3::MachOAnalyzer* ma,
886 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
887 __block Node protocols;
888
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});
893 }
894 };
895
896 ma->forEachObjCMethod(methodListVMAddr, vmAddrConverter, visitMethod);
897 };
898
899 auto visitProtocol = ^(Diagnostics& diag, uint64_t protoVMAddr,
900 const dyld3::MachOAnalyzer::ObjCProtocol& objcProto) {
901 const char* protoName = getString(ma, objcProto.nameVMAddr);
902 if (!protoName)
903 return;
904
905 Node entry;
906 entry.map["protocolName"] = Node{protoName};
907
908 if ( objcProto.protocolsVMAddr != 0 ) {
909 __block Node protocols;
910
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});
914 }
915 };
916
917 ma->forEachObjCProtocol(objcProto.protocolsVMAddr, vmAddrConverter, visitProtocol);
918 if (!protocols.array.empty()) {
919 entry.map["protocols"] = protocols;
920 }
921 }
922
923 Node methods;
924 getMethods(ma, objcProto.instanceMethodsVMAddr, instancePrefix, methods);
925 getMethods(ma, objcProto.classMethodsVMAddr, classPrefix, methods);
926 if (!methods.array.empty()) {
927 entry.map["methods"] = methods;
928 }
929
930 Node optMethods;
931 getMethods(ma, objcProto.optionalInstanceMethodsVMAddr, instancePrefix, optMethods);
932 getMethods(ma, objcProto.optionalClassMethodsVMAddr, classPrefix, optMethods);
933 if (!optMethods.array.empty()) {
934 entry.map["optionalMethods"] = optMethods;
935 }
936
937 protocols.array.push_back(entry);
938 };
939
940 Diagnostics diag;
941 ma->forEachObjCProtocol(diag, vmAddrConverter, visitProtocol);
942
943 return protocols.array.empty() ? std::optional<Node>() : protocols;
944 };
945
946 auto getSelRefs = ^(const dyld3::MachOAnalyzer* ma,
947 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
948 __block std::vector<const char *> selNames;
949
950 auto visitSelRef = ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr) {
951 if (auto selValue = getString(ma, selRefTargetVMAddr)) {
952 selNames.push_back(selValue);
953 }
954 };
955
956 Diagnostics diag;
957 ma->forEachObjCSelectorReference(diag, vmAddrConverter, visitSelRef);
958
959 std::sort(selNames.begin(), selNames.end(),
960 [](const char* a, const char* b) {
961 return strcasecmp(a, b) < 0;
962 });
963
964 Node selrefs;
965 for (auto s: selNames) {
966 selrefs.array.push_back(Node{s});
967 }
968
969 return selrefs.array.empty() ? std::optional<Node>() : selrefs;
970 };
971
972 auto getClasses = ^(const dyld3::MachOAnalyzer* ma,
973 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
974 Diagnostics diag;
975 const uint32_t pointerSize = ma->pointerSize();
976
977 // Get the vmAddrs for all exported symbols as we want to know if classes
978 // are exported
979 std::set<uint64_t> exportedSymbolVMAddrs;
980 {
981 uint64_t loadAddress = ma->preferredLoadAddress();
982
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);
992 }
993 }
994 }
995 }
996
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) {
1002 if (isMetaClass) {
1003 if (skippedPreviousClass) {
1004 // If the class was bad, then skip the meta class too
1005 skippedPreviousClass = false;
1006 return;
1007 }
1008 } else {
1009 skippedPreviousClass = true;
1010 }
1011
1012 std::string classType = "-";
1013 if (isMetaClass)
1014 classType = "+";
1015 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
1016 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
1017 if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) {
1018 return;
1019 }
1020
1021 const char* superClassName = nullptr;
1022 if ( ma->inDyldCache() ) {
1023 if ( objcClass.superclassVMAddr != 0 ) {
1024 if (isMetaClass) {
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;
1031 } else {
1032 auto it = metaclassVMAddrToName.find(objcClass.superclassVMAddr);
1033 assert(it != metaclassVMAddrToName.end());
1034 superClassName = it->second;
1035 }
1036 } else {
1037 auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
1038 assert(it != classVMAddrToName.end());
1039 superClassName = it->second;
1040 }
1041 }
1042 } else {
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];
1050 if (isMetaClass) {
1051 if ( strstr(symbolName, "_OBJC_METACLASS_$_") == symbolName ) {
1052 superClassName = symbolName + strlen("_OBJC_METACLASS_$_");
1053 } else {
1054 // Swift classes don't start with these prefixes so just skip them
1055 if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
1056 return;
1057 }
1058 } else {
1059 if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
1060 superClassName = symbolName + strlen("_OBJC_CLASS_$_");
1061 } else {
1062 // Swift classes don't start with these prefixes so just skip them
1063 if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
1064 return;
1065 }
1066 }
1067 } else {
1068 // Rebase within this image.
1069 if (isMetaClass) {
1070 auto it = onDiskMetaclassVMAddrToName.find(objcClass.superclassVMAddr);
1071 assert(it != onDiskMetaclassVMAddrToName.end());
1072 superClassName = it->second;
1073 } else {
1074 auto it = onDiskClassVMAddrToName.find(objcClass.superclassVMAddr);
1075 assert(it != onDiskClassVMAddrToName.end());
1076 superClassName = it->second;
1077 }
1078 }
1079 }
1080
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)
1087 return;
1088 methodsNode.array.push_back(Node{classType + methodName});
1089 };
1090 ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter,
1091 visitMethod);
1092
1093 std::optional<Node> properties = getProperties(ma, objcClass.basePropertiesVMAddr(pointerSize), vmAddrConverter);
1094
1095 if (isMetaClass) {
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());
1104 }
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());
1110 }
1111 return;
1112 }
1113
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();
1124
1125 currentClassNode.map["exported"] = Node{exportedSymbolVMAddrs.count(classVMAddr) != 0};
1126
1127 // We didn't skip this class so mark it as such
1128 skippedPreviousClass = false;
1129
1130 classesNode.array.push_back(currentClassNode);
1131 };
1132
1133 ma->forEachObjCClass(diag, vmAddrConverter, visitClass);
1134 return classesNode.array.empty() ? std::optional<Node>() : classesNode;
1135 };
1136
1137 auto getCategories = ^(const dyld3::MachOAnalyzer* ma,
1138 const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
1139 Diagnostics diag;
1140
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)
1147 return;
1148
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;
1154 } else {
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_$_");
1164 } else {
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
1167 return;
1168 }
1169 } else {
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
1173 return;
1174 }
1175 className = it->second;
1176 }
1177 }
1178
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});
1184 };
1185 ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter,
1186 visitInstanceMethod);
1187
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});
1193 };
1194 ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter,
1195 visitClassMethod);
1196
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();
1206
1207 categoriesNode.array.push_back(currentCategoryNode);
1208 };
1209
1210 ma->forEachObjCCategory(diag, vmAddrConverter, visitCategory);
1211 return categoriesNode.array.empty() ? std::optional<Node>() : categoriesNode;
1212 };
1213
1214 __block bool needsComma = false;
1215
1216 dyld3::json::streamArrayBegin(needsComma);
1217
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;
1221
1222 Node imageRecord;
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);
1229
1230 // Skip emitting images with no objc data
1231 if (!classes.has_value() && !categories.has_value() && !protocols.has_value() && !selrefs.has_value())
1232 return;
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();
1241
1242 dyld3::json::streamArrayNode(needsComma, imageRecord);
1243 });
1244
1245 FileSystemPhysical fileSystem;
1246 dyld3::Platform platform = dyldCache->platform();
1247 const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(dyldCache->archName(), true);
1248
1249 dyldCache->forEachLaunchClosure(^(const char *executableRuntimePath, const dyld3::closure::LaunchClosure *closure) {
1250 Diagnostics diag;
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();
1255
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);
1260 });
1261 if ( diag.hasError() )
1262 return;
1263
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))) {
1271 if (isMetaClass)
1272 onDiskMetaclassVMAddrToName[classVMAddr] = className;
1273 else
1274 onDiskClassVMAddrToName[classVMAddr] = className;
1275 }
1276 };
1277
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);
1280
1281 ma->forEachObjCClass(diag, onDiskVMAddrConverter, visitClass);
1282
1283 Node imageRecord;
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);
1288 // TODO: protocols
1289 std::optional<Node> selrefs = getSelRefs(ma, onDiskVMAddrConverter);
1290
1291 // Skip emitting images with no objc data
1292 if (!classes.has_value() && !categories.has_value() && !selrefs.has_value())
1293 return;
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();
1300
1301 dyld3::json::streamArrayNode(needsComma, imageRecord);
1302 });
1303
1304 dyldCache->forEachDlopenImage(^(const char *runtimePath, const dyld3::closure::Image *image) {
1305 Diagnostics diag;
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();
1310
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);
1315 });
1316 if ( diag.hasError() )
1317 return;
1318
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))) {
1326 if (isMetaClass)
1327 onDiskMetaclassVMAddrToName[classVMAddr] = className;
1328 else
1329 onDiskClassVMAddrToName[classVMAddr] = className;
1330 }
1331 };
1332
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);
1335
1336 ma->forEachObjCClass(diag, onDiskVMAddrConverter, visitClass);
1337
1338 Node imageRecord;
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);
1343 // TODO: protocols
1344 std::optional<Node> selrefs = getSelRefs(ma, onDiskVMAddrConverter);
1345
1346 // Skip emitting images with no objc data
1347 if (!classes.has_value() && !categories.has_value() && !selrefs.has_value())
1348 return;
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();
1355
1356 dyld3::json::streamArrayNode(needsComma, imageRecord);
1357 });
1358
1359 dyld3::json::streamArrayEnd(needsComma);
1360 }
1361 else if ( options.mode == modeObjCSelectors ) {
1362 if ( dyldCache->objcOpt() == nullptr ) {
1363 fprintf(stderr, "Error: could not get optimized objc\n");
1364 return 1;
1365 }
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");
1369 return 1;
1370 }
1371
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];
1375 if ( offset == 0 )
1376 continue;
1377 const char* selName = selectors->getEntryForIndex((uint32_t)index);
1378 selNames.push_back(selName);
1379 }
1380
1381 std::sort(selNames.begin(), selNames.end(),
1382 [](const char* a, const char* b) {
1383 // Sort by offset, not string value
1384 return a < b;
1385 });
1386
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};
1392
1393 root.array.push_back(selNode);
1394 }
1395
1396 dyld3::json::printJSON(root, 0, std::cout);
1397 }
1398 else if ( options.mode == modeExtract ) {
1399 return dyld_shared_cache_extract_dylibs(sharedCachePath, options.extractionDir);
1400 }
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");
1404 return 1;
1405 }
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;
1410
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);
1419 }
1420 });
1421
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");
1424 return 1;
1425 }
1426
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));
1429 return 1;
1430 }
1431
1432 dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(contentRebased);
1433
1434 uint64_t selectorStringVMAddrStart = vmAddrConverter.convertToVMAddr(((uint64_t*)objcCacheOffsets)[0]);
1435 uint64_t selectorStringVMAddrEnd = vmAddrConverter.convertToVMAddr(((uint64_t*)objcCacheOffsets)[1]);
1436
1437 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
1438 if (diag.hasError())
1439 return;
1440
1441 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
1442 intptr_t slide = ma->getSlide();
1443
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,
1449 bool isMetaClass) {
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;
1456 });
1457 });
1458
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;
1464 });
1465
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;
1470 });
1471 });
1472 });
1473 if (diag.hasError())
1474 return 1;
1475
1476 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
1477 if (diag.hasError())
1478 return;
1479
1480 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
1481 intptr_t slide = ma->getSlide();
1482
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,
1488 bool isMetaClass) {
1489 const char* type = "class";
1490 if (isMetaClass)
1491 type = "meta-class";
1492 const char* className = (const char*)objcClass.nameVMAddr(pointerSize) + slide;
1493
1494 if (objcClass.methodCacheVMAddr == 0) {
1495 printf("%s (%s): empty\n", className, type);
1496 return;
1497 }
1498
1499 struct Bucket {
1500 uint32_t selOffset;
1501 uint32_t impOffset;
1502 };
1503 struct ImpCache {
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[];
1511 };
1512
1513 const ImpCache* impCache = (const ImpCache*)(objcClass.methodCacheVMAddr + slide);
1514 printf("%s (%s): %d buckets\n", className, type, impCache->cache_mask + 1);
1515
1516 if ((classVMAddr + impCache->fallback_class_offset) != objcClass.superclassVMAddr) {
1517 printf("Flattening fallback: %s\n", classVMAddrToNameMap[classVMAddr + impCache->fallback_class_offset]);
1518 }
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) {
1525 // Empty bucket
1526 printf(" - 0x%016llx: %s\n", 0ULL, "");
1527 } else {
1528 assert(sel < selectorStringVMAddrEnd);
1529
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));
1533 }
1534 assert(it != methodToClassMap.end());
1535 printf(" - 0x%016llx: %s (from %s)\n", imp, (const char*)(sel + slide), it->second);
1536 }
1537 }
1538 });
1539 });
1540 } else {
1541 switch ( options.mode ) {
1542 case modeList: {
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);
1555 }
1556 indexToPaths.push_back(empty);
1557 indexToPaths.back().insert(installName);
1558 });
1559 dyldCache->forEachDylibPath(^(const char* dylibPath, uint32_t index) {
1560 indexToPaths[index].insert(dylibPath);
1561 });
1562 int index = 0;
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());
1570 }
1571 ++index;
1572 }
1573 break;
1574 }
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);
1581 stop = true;
1582 }
1583 });
1584 });
1585 break;
1586 }
1587 case modeMap: {
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;
1597 }
1598 });
1599 });
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);
1606 }
1607 lastEnd = entry.second;
1608 }
1609 break;
1610 }
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 )
1615 return;
1616 dependentTargetFound = true;
1617
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);
1631 }
1632 else {
1633 printf("\n");
1634 }
1635 }
1636 else {
1637 printf("\t%s\n", loadPath);
1638 }
1639 };
1640
1641 dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
1642
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, &currentVersion) ) {
1648 printDep(dylibInstallName, compatVersion, currentVersion);
1649 }
1650
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);
1654 });
1655 });
1656 if (options.dependentsOfPath && !dependentTargetFound) {
1657 fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath);
1658 exit(1);
1659 }
1660 break;
1661 }
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);
1669 }
1670 else {
1671 const char* oldMessage = pos->second;
1672 char* newMesssage;
1673 asprintf(&newMesssage, "%s, %s", oldMessage, message);
1674 pageToContent[p] = newMesssage;
1675 ::free((void*)oldMessage);
1676 }
1677 }
1678 };
1679
1680 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1681 dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1682 Diagnostics diag;
1683 dyld3::MachOAnalyzer::LinkEditInfo leInfo;
1684 ma->getLinkEditPointers(diag, leInfo);
1685
1686 if (diag.hasError())
1687 return;
1688
1689 char message[1000];
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);
1698 }
1699 // add binding info
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);
1705 }
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);
1712 }
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);
1719 }
1720 });
1721
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);
1724 }
1725 break;
1726 }
1727 case modeSize: {
1728 struct TextInfo {
1729 uint64_t textSize;
1730 const char* path;
1731 };
1732 __block std::vector<TextInfo> textSegments;
1733 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1734
1735 dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1736 ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
1737 if ( strcmp(info.segName, "__TEXT") != 0 )
1738 return;
1739 textSegments.push_back({ info.fileSize, installName });
1740 });
1741 });
1742 std::sort(textSegments.begin(), textSegments.end(), [](const TextInfo& left, const TextInfo& right) {
1743 return (left.textSize > right.textSize);
1744 });
1745 for (std::vector<TextInfo>::iterator it = textSegments.begin(); it != textSegments.end(); ++it) {
1746 printf(" 0x%08llX %s\n", it->textSize, it->path);
1747 }
1748 break;
1749 }
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);
1764 else
1765 printf(" used by: %s+0x%04llX (addend=%d) in %s\n", usageAt.segName, patchLocVmAddr-usageAt.vmAddr, patchLocation.addend, usageAt.installName);
1766 });
1767 });
1768 ++imageIndex;
1769 });
1770 break;
1771 }
1772 case modeNone:
1773 case modeInfo:
1774 case modeSlideInfo:
1775 case modeVerboseSlideInfo:
1776 case modeTextInfo:
1777 case modeLocalSymbols:
1778 case modeJSONMap:
1779 case modeJSONDependents:
1780 case modeSectionSizes:
1781 case modeStrings:
1782 case modeObjCProtocols:
1783 case modeObjCImpCaches:
1784 case modeObjCClasses:
1785 case modeObjCSelectors:
1786 case modeExtract:
1787 break;
1788 }
1789 }
1790 return 0;
1791 }