dyld-750.6.tar.gz
[apple/dyld.git] / launch-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
42 #include <TargetConditionals.h>
43
44 #include <map>
45 #include <vector>
46 #include <iostream>
47 #include <optional>
48
49 #include "ClosureBuilder.h"
50 #include "DyldSharedCache.h"
51 #include "ClosureFileSystemPhysical.h"
52 #include "JSONWriter.h"
53 #include "Trie.hpp"
54
55 #include "objc-shared-cache.h"
56
57 #if TARGET_OS_OSX
58 #define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle"
59 #else
60 #define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle"
61 #endif
62
63 using dyld3::closure::ClosureBuilder;
64 using dyld3::closure::FileSystemPhysical;
65
66 // mmap() an shared cache file read/only but laid out like it would be at runtime
67 static const DyldSharedCache* mapCacheFile(const char* path)
68 {
69 struct stat statbuf;
70 if ( ::stat(path, &statbuf) ) {
71 fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
72 return nullptr;
73 }
74
75 int cache_fd = ::open(path, O_RDONLY);
76 if (cache_fd < 0) {
77 fprintf(stderr, "Error: failed to open shared cache file at %s\n", path);
78 return nullptr;
79 }
80
81 uint8_t firstPage[4096];
82 if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
83 fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
84 return nullptr;
85 }
86 const dyld_cache_header* header = (dyld_cache_header*)firstPage;
87 const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
88
89 size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
90 vm_address_t result;
91 kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
92 if ( r != KERN_SUCCESS ) {
93 fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
94 return nullptr;
95 }
96 for (int i=0; i < 3; ++i) {
97 void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
98 PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
99 if (mapped_cache == MAP_FAILED) {
100 fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", path, errno);
101 return nullptr;
102 }
103 }
104 ::close(cache_fd);
105
106 return (DyldSharedCache*)result;
107 }
108
109 enum Mode {
110 modeNone,
111 modeList,
112 modeMap,
113 modeDependencies,
114 modeSlideInfo,
115 modeVerboseSlideInfo,
116 modeAcceleratorInfo,
117 modeTextInfo,
118 modeLinkEdit,
119 modeLocalSymbols,
120 modeJSONMap,
121 modeJSONDependents,
122 modeSectionSizes,
123 modeStrings,
124 modeInfo,
125 modeSize,
126 modeObjCProtocols,
127 modeObjCClasses,
128 modeObjCSelectors,
129 modeExtract
130 };
131
132 struct Options {
133 Mode mode;
134 const char* dependentsOfPath;
135 const char* extractionDir;
136 bool printUUIDs;
137 bool printVMAddrs;
138 bool printDylibVersions;
139 bool printInodes;
140 };
141
142
143 void usage() {
144 fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map | -slide_info | -verbose_slide_info | -info | -extract <dylib-dir> [ shared-cache-file ] \n");
145 }
146
147 static void checkMode(Mode mode) {
148 if ( mode != modeNone ) {
149 fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n");
150 usage();
151 exit(1);
152 }
153 }
154
155 static bool isAlias(const char* path, const DyldSharedCache* dyldCache) {
156 const dyld_cache_header* header = &dyldCache->header;
157 const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
158 const dyld_cache_mapping_info* textMapping = &mappings[0];
159 // paths for aliases are store between cache header and first segment
160 return path < (char*)textMapping;
161 }
162
163 int main (int argc, const char* argv[]) {
164
165 const char* sharedCachePath = nullptr;
166
167 Options options;
168 options.mode = modeNone;
169 options.printUUIDs = false;
170 options.printVMAddrs = false;
171 options.printDylibVersions = false;
172 options.printInodes = false;
173 options.dependentsOfPath = NULL;
174 options.extractionDir = NULL;
175
176 bool printStrings = false;
177 bool printExports = false;
178
179 for (uint32_t i = 1; i < argc; i++) {
180 const char* opt = argv[i];
181 if (opt[0] == '-') {
182 if (strcmp(opt, "-list") == 0) {
183 checkMode(options.mode);
184 options.mode = modeList;
185 }
186 else if (strcmp(opt, "-dependents") == 0) {
187 checkMode(options.mode);
188 options.mode = modeDependencies;
189 options.dependentsOfPath = argv[++i];
190 if ( i >= argc ) {
191 fprintf(stderr, "Error: option -depdendents requires an argument\n");
192 usage();
193 exit(1);
194 }
195 }
196 else if (strcmp(opt, "-linkedit") == 0) {
197 checkMode(options.mode);
198 options.mode = modeLinkEdit;
199 }
200 else if (strcmp(opt, "-info") == 0) {
201 checkMode(options.mode);
202 options.mode = modeInfo;
203 }
204 else if (strcmp(opt, "-slide_info") == 0) {
205 checkMode(options.mode);
206 options.mode = modeSlideInfo;
207 }
208 else if (strcmp(opt, "-verbose_slide_info") == 0) {
209 checkMode(options.mode);
210 options.mode = modeVerboseSlideInfo;
211 }
212 else if (strcmp(opt, "-accelerator_info") == 0) {
213 checkMode(options.mode);
214 options.mode = modeAcceleratorInfo;
215 }
216 else if (strcmp(opt, "-text_info") == 0) {
217 checkMode(options.mode);
218 options.mode = modeTextInfo;
219 }
220 else if (strcmp(opt, "-local_symbols") == 0) {
221 checkMode(options.mode);
222 options.mode = modeLocalSymbols;
223 }
224 else if (strcmp(opt, "-strings") == 0) {
225 if (options.mode != modeStrings)
226 checkMode(options.mode);
227 options.mode = modeStrings;
228 printStrings = true;
229 }
230 else if (strcmp(opt, "-sections") == 0) {
231 checkMode(options.mode);
232 options.mode = modeSectionSizes;
233 }
234 else if (strcmp(opt, "-exports") == 0) {
235 if (options.mode != modeStrings)
236 checkMode(options.mode);
237 options.mode = modeStrings;
238 printExports = true;
239 }
240 else if (strcmp(opt, "-map") == 0) {
241 checkMode(options.mode);
242 options.mode = modeMap;
243 }
244 else if (strcmp(opt, "-json-map") == 0) {
245 checkMode(options.mode);
246 options.mode = modeJSONMap;
247 }
248 else if (strcmp(opt, "-json-dependents") == 0) {
249 checkMode(options.mode);
250 options.mode = modeJSONDependents;
251 }
252 else if (strcmp(opt, "-size") == 0) {
253 checkMode(options.mode);
254 options.mode = modeSize;
255 }
256 else if (strcmp(opt, "-objc-protocols") == 0) {
257 checkMode(options.mode);
258 options.mode = modeObjCProtocols;
259 }
260 else if (strcmp(opt, "-objc-classes") == 0) {
261 checkMode(options.mode);
262 options.mode = modeObjCClasses;
263 }
264 else if (strcmp(opt, "-objc-selectors") == 0) {
265 checkMode(options.mode);
266 options.mode = modeObjCSelectors;
267 }
268 else if (strcmp(opt, "-extract") == 0) {
269 checkMode(options.mode);
270 options.mode = modeExtract;
271 options.extractionDir = argv[++i];
272 if ( i >= argc ) {
273 fprintf(stderr, "Error: option -extract requires a directory argument\n");
274 usage();
275 exit(1);
276 }
277 }
278 else if (strcmp(opt, "-uuid") == 0) {
279 options.printUUIDs = true;
280 }
281 else if (strcmp(opt, "-inode") == 0) {
282 options.printInodes = true;
283 }
284 else if (strcmp(opt, "-versions") == 0) {
285 options.printDylibVersions = true;
286 }
287 else if (strcmp(opt, "-vmaddr") == 0) {
288 options.printVMAddrs = true;
289 }
290 else {
291 fprintf(stderr, "Error: unrecognized option %s\n", opt);
292 usage();
293 exit(1);
294 }
295 }
296 else {
297 sharedCachePath = opt;
298 }
299 }
300
301 if ( options.mode == modeNone ) {
302 fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
303 usage();
304 exit(1);
305 }
306
307 if ( options.mode != modeSlideInfo && options.mode != modeVerboseSlideInfo ) {
308 if ( options.printUUIDs && (options.mode != modeList) )
309 fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
310
311 if ( options.printVMAddrs && (options.mode != modeList) )
312 fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
313
314 if ( options.printDylibVersions && (options.mode != modeDependencies) )
315 fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
316
317 if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) {
318 fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
319 usage();
320 exit(1);
321 }
322 }
323
324 const DyldSharedCache* dyldCache = nullptr;
325 if ( sharedCachePath != nullptr ) {
326 dyldCache = mapCacheFile(sharedCachePath);
327 // mapCacheFile prints an error if something goes wrong, so just return in that case.
328 if ( dyldCache == nullptr )
329 return 1;
330 }
331 else {
332 #if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
333 size_t cacheLength;
334 dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
335 #endif
336 if (dyldCache == nullptr) {
337 fprintf(stderr, "Could not get in-memory shared cache\n");
338 return 1;
339 }
340 if ( options.mode == modeObjCClasses ) {
341 fprintf(stderr, "Cannot use -objc-info with a live cache. Please run with a path to an on-disk cache file\n");
342 return 1;
343 }
344 }
345
346 if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) {
347 const dyld_cache_header* header = &dyldCache->header;
348 if ( header->slideInfoOffset == 0 ) {
349 fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
350 exit(1);
351 }
352 const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
353 const dyld_cache_mapping_info* dataMapping = &mappings[1];
354 uint64_t dataStartAddress = dataMapping->address;
355 uint64_t dataSize = dataMapping->size;
356
357 const dyld_cache_slide_info* slideInfoHeader = dyldCache->slideInfo();
358 printf("slide info version=%d\n", slideInfoHeader->version);
359 if ( slideInfoHeader->version == 1 ) {
360 printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count, dataSize/4096);
361 const dyld_cache_slide_info_entry* entries = (dyld_cache_slide_info_entry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset);
362 const uint16_t* tocs = (uint16_t*)((char*)slideInfoHeader + slideInfoHeader->toc_offset);
363 for(int i=0; i < slideInfoHeader->toc_count; ++i) {
364 printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, tocs[i]);
365 const dyld_cache_slide_info_entry* entry = &entries[tocs[i]];
366 for(int j=0; j < slideInfoHeader->entries_size; ++j)
367 printf("%02X", entry->bits[j]);
368 printf("\n");
369 }
370 }
371 else if ( slideInfoHeader->version == 2 ) {
372 const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(slideInfoHeader);
373 printf("page_size=%d\n", slideInfo->page_size);
374 printf("delta_mask=0x%016llX\n", slideInfo->delta_mask);
375 printf("value_add=0x%016llX\n", slideInfo->value_add);
376 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count, slideInfo->page_extras_count);
377 const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset);
378 const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset);
379 for (int i=0; i < slideInfo->page_starts_count; ++i) {
380 const uint16_t start = starts[i];
381 auto rebaseChain = [&](uint8_t* pageContent, uint16_t startOffset)
382 {
383 uintptr_t slideAmount = 0;
384 const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask);
385 const uintptr_t valueMask = ~deltaMask;
386 const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add);
387 const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
388
389 uint32_t pageOffset = startOffset;
390 uint32_t delta = 1;
391 while ( delta != 0 ) {
392 uint8_t* loc = pageContent + pageOffset;
393 uintptr_t rawValue = *((uintptr_t*)loc);
394 delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
395 uintptr_t value = (rawValue & valueMask);
396 if ( value != 0 ) {
397 value += valueAdd;
398 value += slideAmount;
399 }
400 printf(" [% 5d + 0x%04llX]: 0x%016llX = 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue, (uint64_t)value);
401 pageOffset += delta;
402 }
403 };
404 const uint8_t* dataPagesStart = dyldCache->dataRegionStart();
405 if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
406 printf("page[% 5d]: no rebasing\n", i);
407 }
408 else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
409 printf("page[% 5d]: ", i);
410 int j=(start & 0x3FFF);
411 bool done = false;
412 do {
413 uint16_t aStart = extras[j];
414 printf("start=0x%04X ", aStart & 0x3FFF);
415 if ( options.mode == modeVerboseSlideInfo ) {
416 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
417 uint16_t pageStartOffset = (aStart & 0x3FFF)*4;
418 rebaseChain(page, pageStartOffset);
419 }
420 done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
421 ++j;
422 } while ( !done );
423 printf("\n");
424 }
425 else {
426 printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
427 if ( options.mode == modeVerboseSlideInfo ) {
428 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
429 uint16_t pageStartOffset = start*4;
430 rebaseChain(page, pageStartOffset);
431 }
432 }
433 }
434 }
435 else if ( slideInfoHeader->version == 3 ) {
436 const dyld_cache_slide_info3* slideInfo = (dyld_cache_slide_info3*)(slideInfoHeader);
437 printf("page_size=%d\n", slideInfo->page_size);
438 printf("page_starts_count=%d\n", slideInfo->page_starts_count);
439 printf("auth_value_add=0x%016llX\n", slideInfo->auth_value_add);
440 const uintptr_t authValueAdd = (uintptr_t)(slideInfo->auth_value_add);
441 const uint8_t* dataSegmentStart = dyldCache->dataRegionStart();
442 for (int i=0; i < slideInfo->page_starts_count; ++i) {
443 uint16_t delta = slideInfo->page_starts[i];
444 if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
445 printf("page[% 5d]: no rebasing\n", i);
446 continue;
447 }
448
449 printf("page[% 5d]: start=0x%04X\n", i, delta);
450 if ( options.mode != modeVerboseSlideInfo )
451 continue;
452
453 delta = delta/sizeof(uint64_t); // initial offset is byte based
454 const uint8_t* pageStart = dataSegmentStart + (i * slideInfo->page_size);
455 const dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)pageStart;
456 do {
457 loc += delta;
458 delta = loc->plain.offsetToNextPointer;
459 dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
460 ptr.raw64 = *((uint64_t*)loc);
461 if ( loc->auth.authenticated ) {
462 uint64_t target = authValueAdd + loc->auth.offsetFromSharedCacheBase;
463 uint64_t targetValue = ptr.arm64e.signPointer((void*)loc, target);
464 printf(" [% 5d + 0x%04llX]: 0x%016llX (JOP: diversity %d, address %s, %s)\n",
465 i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue,
466 ptr.arm64e.authBind.diversity, ptr.arm64e.authBind.addrDiv ? "true" : "false",
467 ptr.arm64e.keyName());
468 }
469 else {
470 uint64_t targetValue = ptr.arm64e.unpackTarget();
471 printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue);
472 }
473 } while (delta != 0);
474 }
475 }
476 else if ( slideInfoHeader->version == 4 ) {
477 const dyld_cache_slide_info4* slideInfo = (dyld_cache_slide_info4*)(slideInfoHeader);
478 printf("page_size=%d\n", slideInfo->page_size);
479 printf("delta_mask=0x%016llX\n", slideInfo->delta_mask);
480 printf("value_add=0x%016llX\n", slideInfo->value_add);
481 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count, slideInfo->page_extras_count);
482 const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset);
483 const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset);
484 for (int i=0; i < slideInfo->page_starts_count; ++i) {
485 const uint16_t start = starts[i];
486 auto rebaseChainV4 = [&](uint8_t* pageContent, uint16_t startOffset)
487 {
488 uintptr_t slideAmount = 0;
489 const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask);
490 const uintptr_t valueMask = ~deltaMask;
491 const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add);
492 const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
493
494 uint32_t pageOffset = startOffset;
495 uint32_t delta = 1;
496 while ( delta != 0 ) {
497 uint8_t* loc = pageContent + pageOffset;
498 uint32_t rawValue = *((uint32_t*)loc);
499 delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
500 uintptr_t value = (rawValue & valueMask);
501 if ( (value & 0xFFFF8000) == 0 ) {
502 // small positive non-pointer, use as-is
503 }
504 else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
505 // small negative non-pointer
506 value |= 0xC0000000;
507 }
508 else {
509 value += valueAdd;
510 value += slideAmount;
511 }
512 printf(" [% 5d + 0x%04X]: 0x%08X\n", i, pageOffset, rawValue);
513 pageOffset += delta;
514 }
515 };
516 const uint8_t* dataPagesStart = dyldCache->dataRegionStart();
517 if ( start == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
518 printf("page[% 5d]: no rebasing\n", i);
519 }
520 else if ( start & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
521 printf("page[% 5d]: ", i);
522 int j=(start & DYLD_CACHE_SLIDE4_PAGE_INDEX);
523 bool done = false;
524 do {
525 uint16_t aStart = extras[j];
526 printf("start=0x%04X ", aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX);
527 if ( options.mode == modeVerboseSlideInfo ) {
528 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
529 uint16_t pageStartOffset = (aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX)*4;
530 rebaseChainV4(page, pageStartOffset);
531 }
532 done = (extras[j] & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
533 ++j;
534 } while ( !done );
535 printf("\n");
536 }
537 else {
538 printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
539 if ( options.mode == modeVerboseSlideInfo ) {
540 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
541 uint16_t pageStartOffset = start*4;
542 rebaseChainV4(page, pageStartOffset);
543 }
544 }
545 }
546 }
547 }
548 else if ( options.mode == modeInfo ) {
549 const dyld_cache_header* header = &dyldCache->header;
550 printf("uuid: ");
551 if ( header->mappingOffset >= 0x68 ) {
552 const uint8_t* uuid = header->uuid;
553 printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
554 uuid[0], uuid[1], uuid[2], uuid[3],
555 uuid[4], uuid[5], uuid[6], uuid[7],
556 uuid[8], uuid[9], uuid[10], uuid[11],
557 uuid[12], uuid[13], uuid[14], uuid[15]);
558 }
559 else {
560 printf("n/a\n");
561 }
562 if ( header->mappingOffset >= 0xE0 ) {
563 // HACK until this uses new header
564 uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8));
565 uint32_t bitfield = *((uint32_t*)(((char*)header) + 0xDC));
566 uint32_t simulator = bitfield & 0x200;
567 uint32_t locallyBuiltCache = bitfield & 0x400;
568 switch (platform) {
569 case 1:
570 printf("platform: macOS\n");
571 break;
572 case 2:
573 if ( simulator )
574 printf("platform: iOS simulator\n");
575 else
576 printf("platform: iOS\n");
577 break;
578 case 3:
579 if ( simulator )
580 printf("platform: tvOS simulator\n");
581 else
582 printf("platform: tvOS\n");
583 break;
584 case 4:
585 if ( simulator )
586 printf("platform: watchOS simulator\n");
587 else
588 printf("platform: watchOS\n");
589 break;
590 case 5:
591 printf("platform: bridgeOS\n");
592 break;
593 default:
594 printf("platform: 0x%08X 0x%08X\n", platform, simulator);
595 }
596 printf("built by: %s\n", locallyBuiltCache ? "local machine" : "B&I");
597 }
598 printf("cache type: %s\n", header->cacheType ? "production" : "development");
599 printf("image count: %u\n", header->imagesCount);
600 if ( (header->mappingOffset >= 0x78) && (header->branchPoolsOffset != 0) ) {
601 printf("branch pool count: %u\n", header->branchPoolsCount);
602 }
603 if ( header->slideInfoSize > 0 ) {
604 uint32_t pageSize = 0x4000; // fix me for intel
605 uint32_t possibleSlideValues = (uint32_t)(header->maxSlide/pageSize);
606 uint32_t entropyBits = 32 - __builtin_clz(possibleSlideValues - 1);
607 printf("ASLR entropy: %u-bits\n", entropyBits);
608 }
609 printf("mappings:\n");
610 const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
611 for (uint32_t i=0; i < header->mappingCount; ++i) {
612 if ( mappings[i].initProt & VM_PROT_EXECUTE )
613 printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
614 mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
615 mappings[i].address, mappings[i].address + mappings[i].size);
616 else if ( mappings[i].initProt & VM_PROT_WRITE )
617 printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
618 mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
619 mappings[i].address, mappings[i].address + mappings[i].size);
620 else if ( mappings[i].initProt & VM_PROT_READ )
621 printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
622 mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
623 mappings[i].address, mappings[i].address + mappings[i].size);
624 }
625 if ( header->codeSignatureOffset != 0 ) {
626 uint64_t size = header->codeSignatureSize;
627 uint64_t csAddr = mappings[header->mappingCount-1].address + mappings[header->mappingCount-1].size;
628 if ( size != 0 )
629 printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
630 size/(1024*1024), header->codeSignatureOffset, header->codeSignatureOffset + size, csAddr, csAddr + size);
631 }
632 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
633 header->slideInfoSize/1024, header->slideInfoOffset, header->slideInfoOffset + header->slideInfoSize);
634 if ( header->localSymbolsOffset != 0 )
635 printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
636 header->localSymbolsSize/(1024*1024), header->localSymbolsOffset, header->localSymbolsOffset + header->localSymbolsSize);
637 if ( (header->mappingOffset >= 0x78) && (header->accelerateInfoSize != 0) )
638 printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n",
639 header->accelerateInfoSize/1024, header->accelerateInfoAddr, header->accelerateInfoAddr + header->accelerateInfoSize);
640 }
641 else if ( options.mode == modeAcceleratorInfo ) {
642 const dyld_cache_header* header = &dyldCache->header;
643 if ( (header->mappingOffset < sizeof(dyld_cache_header)) || (header->accelerateInfoSize == 0) ) {
644 printf("no accelerator info\n");
645 }
646 else {
647 const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
648 uint64_t aiAddr = header->accelerateInfoAddr;
649 const dyld_cache_accelerator_info* accelInfo = NULL;
650 for (uint32_t i=0; i < header->mappingCount; ++i) {
651 if ( (mappings[i].address <= aiAddr) && (aiAddr < mappings[i].address+mappings[i].size) ) {
652 uint64_t offset = aiAddr - mappings[i].address + mappings[i].fileOffset;
653 accelInfo = (dyld_cache_accelerator_info*)((uint8_t*)dyldCache + offset);
654 }
655 }
656 if ( accelInfo == NULL ) {
657 printf("accelerator info not in any mapped range\n");
658 }
659 else {
660 const dyld_cache_image_info* images = (dyld_cache_image_info*)((char*)dyldCache + header->imagesOffset);
661 const dyld_cache_image_info_extra* imagesExtra = (dyld_cache_image_info_extra*)((char*)accelInfo + accelInfo->imagesExtrasOffset);
662 const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset);
663 const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset);
664 printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount);
665 for (uint32_t i=0; i < accelInfo->imageExtrasCount; ++i) {
666 printf(" image[%3u] %s:\n", i, (char*)dyldCache +images[i].pathFileOffset);
667 printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr, imagesExtra[i].exportsTrieSize);
668 if ( imagesExtra[i].weakBindingsSize )
669 printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr, imagesExtra[i].weakBindingsSize);
670 printf(" dependents: ");
671 for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex; dependencyArray[d] != 0xFFFF; ++d) {
672 uint16_t depIndex = dependencyArray[d];
673 if ( depIndex & 0x8000 )
674 printf(" up(%d) ", depIndex & 0x7FFF);
675 else
676 printf(" %d ", depIndex);
677 }
678 printf("\n");
679 printf(" re-exports: ");
680 for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex; reExportArray[r] != 0xFFFF; ++r)
681 printf(" %d ", reExportArray[r]);
682 printf("\n");
683 }
684 printf("libdyld.dylib:\n");
685 printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr);
686 printf("initializers (count=%u):\n", accelInfo->initializersCount);
687 const dyld_cache_accelerator_initializer* initializers = (dyld_cache_accelerator_initializer*)((char*)accelInfo + accelInfo->initializersOffset);
688 for (uint32_t i=0; i < accelInfo->initializersCount; ++i) {
689 printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex, mappings[0].address + initializers[i].functionOffset);
690 }
691 printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount);
692 const dyld_cache_accelerator_dof* dofs = (dyld_cache_accelerator_dof*)((char*)accelInfo + accelInfo->dofSectionsOffset);
693 for (uint32_t i=0; i < accelInfo->dofSectionsCount; ++i) {
694 printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex, dofs[i].sectionAddress, dofs[i].sectionAddress+dofs[i].sectionSize);
695 }
696 printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount);
697 const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset);
698 for (uint32_t i=0; i < accelInfo->imageExtrasCount; ++i) {
699 unsigned imageIndex = bottomUpArray[i];
700 if ( imageIndex < accelInfo->imageExtrasCount )
701 printf(" image[%3u] %s\n", imageIndex, (char*)dyldCache + images[imageIndex].pathFileOffset);
702 else
703 printf(" image[%3u] BAD INDEX\n", imageIndex);
704 }
705 printf("range table (count=%u):\n", accelInfo->rangeTableCount);
706 const dyld_cache_range_entry* rangeTable = (dyld_cache_range_entry*)((char*)accelInfo + accelInfo->rangeTableOffset);
707 for (uint32_t i=0; i < accelInfo->rangeTableCount; ++i) {
708 const dyld_cache_range_entry& entry = rangeTable[i];
709 printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress, entry.startAddress + entry.size, (char*)dyldCache + images[entry.imageIndex].pathFileOffset);
710 }
711 printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize);
712 const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset;
713 const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize;
714 std::vector<DylibIndexTrie::Entry> dylibEntries;
715 if ( !Trie<DylibIndex>::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) )
716 printf(" malformed dylibs trie\n");
717 for (const DylibIndexTrie::Entry& x : dylibEntries) {
718 printf(" image[%3u] %s\n", x.info.index, x.name.c_str());
719 }
720 }
721 }
722 }
723 else if ( options.mode == modeTextInfo ) {
724 const dyld_cache_header* header = &dyldCache->header;
725 if ( (header->mappingOffset < sizeof(dyld_cache_header)) || (header->imagesTextCount == 0) ) {
726 printf("no text info\n");
727 }
728 else {
729 const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)dyldCache + header->imagesTextOffset);
730 const dyld_cache_image_text_info* imagesTextEnd = &imagesText[header->imagesTextCount];
731 printf("dylib text infos (count=%llu):\n", header->imagesTextCount);
732 for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) {
733 printf(" 0x%09llX -> 0x%09llX <", p->loadAddress, p->loadAddress + p->textSegmentSize);
734 for (int i=0; i<16; ++i) {
735 switch (i) {
736 case 4:
737 case 6:
738 case 8:
739 case 10:
740 printf("-");
741 break;
742 }
743 printf("%02X", p->uuid[i]);
744 }
745 printf("> %s\n", (char*)dyldCache + p->pathOffset);
746 }
747 }
748 }
749 else if ( options.mode == modeLocalSymbols ) {
750 const dyld_cache_header* header = &dyldCache->header;
751 if ( header->localSymbolsOffset == 0 ) {
752 fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
753 exit(1);
754 }
755 const bool is64 = (strstr((char*)dyldCache, "64") != NULL);
756 const dyld_cache_image_info* imageInfos = (dyld_cache_image_info*)((char*)dyldCache + header->imagesOffset);
757 const dyld_cache_local_symbols_info* localsInfo = (dyld_cache_local_symbols_info*)((char*)dyldCache + header->localSymbolsOffset);
758 const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset + localsInfo->nlistOffset);
759 const uint32_t nlistCount = localsInfo->nlistCount;
760 const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12;
761 const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset + localsInfo->stringsOffset);
762 const uint32_t stringsSize = localsInfo->stringsSize;
763 const uint32_t entriesCount = localsInfo->entriesCount;
764 const dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)((char*)localsInfo + localsInfo->entriesOffset);
765 printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize);
766 printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize);
767 printf("local symbols by dylib (count=%d):\n", entriesCount);
768 //const char* stringPool = (char*)dyldCache + stringsFileOffset;
769 for (int i=0; i < entriesCount; ++i) {
770 const char* imageName = (char*)dyldCache + imageInfos[i].pathFileOffset;
771 printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex, entries[i].nlistCount, imageName);
772 #if 0
773 if ( is64 ) {
774 const nlist_64* symTab = (nlist_64*)((char*)dyldCache + nlistFileOffset);
775 for (int e=0; e < entries[i].nlistCount(); ++e) {
776 const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e];
777 printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]);
778 printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value);
779 }
780 }
781 #endif
782 }
783 }
784 else if ( options.mode == modeJSONMap ) {
785 std::string buffer = dyldCache->generateJSONMap("unknown");
786 printf("%s\n", buffer.c_str());
787 }
788 else if ( options.mode == modeJSONDependents ) {
789 std::cout << dyldCache->generateJSONDependents();
790 }
791 else if ( options.mode == modeStrings ) {
792 if (printStrings) {
793 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
794 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
795 int64_t slide = ma->getSlide();
796 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
797 if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
798 if ( malformedSectionRange ) {
799 stop = true;
800 return;
801 }
802 const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
803 const char* s = (char*)content;
804 const char* end = s + info.sectSize;
805 while ( s < end ) {
806 printf("%s: %s\n", ma->installName(), s);
807 while (*s != '\0' )
808 ++s;
809 ++s;
810 }
811 }
812 });
813 });
814 }
815
816 if (printExports) {
817 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
818 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
819 uint32_t exportTrieRuntimeOffset;
820 uint32_t exportTrieSize;
821 if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) {
822 const uint8_t* start = (uint8_t*)mh + exportTrieRuntimeOffset;
823 const uint8_t* end = start + exportTrieSize;
824 std::vector<ExportInfoTrie::Entry> exports;
825 if ( !ExportInfoTrie::parseTrie(start, end, exports) ) {
826 return;
827 }
828
829 for (const ExportInfoTrie::Entry& entry: exports) {
830 printf("%s: %s\n", ma->installName(), entry.name.c_str());
831 }
832 }
833 });
834 }
835 }
836 else if ( options.mode == modeSectionSizes ) {
837 __block std::map<std::string, uint64_t> sectionSizes;
838 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
839 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
840 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
841 std::string section = std::string(sectInfo.segInfo.segName) + " " + sectInfo.sectName;
842 sectionSizes[section] += sectInfo.sectSize;
843 });
844 });
845 for (const auto& keyAndValue : sectionSizes) {
846 printf("%lld %s\n", keyAndValue.second, keyAndValue.first.c_str());
847 }
848 }
849 else if ( options.mode == modeObjCProtocols ) {
850 if ( dyldCache->objcOpt() == nullptr ) {
851 fprintf(stderr, "Error: could not get optimized objc\n");
852 return 1;
853 }
854 objc_opt::objc_protocolopt2_t* protocols = dyldCache->objcOpt()->protocolopt2();
855 if ( protocols == nullptr ) {
856 fprintf(stderr, "Error: could not get optimized objc protocols\n");
857 return 1;
858 }
859
860 for (uint64_t index = 0; index != protocols->capacity; ++index) {
861 const objc_opt::objc_classheader_t& clshi = protocols->classOffsets()[index];
862 if ( clshi.clsOffset == 0 ) {
863 fprintf(stderr, "[% 5lld]\n", index);
864 continue;
865 }
866 const char* name = (const char*)(((const uint8_t*)protocols) + protocols->offsets()[index]);
867 if ( !clshi.isDuplicate() ) {
868 fprintf(stderr, "[% 5lld] -> (% 8d, % 8d) = %s\n", index, clshi.clsOffset, clshi.hiOffset, name);
869 continue;
870 }
871
872 // class appears in more than one header
873 uint32_t count = clshi.duplicateCount();
874 fprintf(stderr, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n",
875 index, clshi.duplicateIndex(), clshi.duplicateIndex() + clshi.duplicateCount() - 1, name);
876
877 const objc_opt::objc_classheader_t *list = &protocols->duplicateOffsets()[clshi.duplicateIndex()];
878 for (uint32_t i = 0; i < count; i++) {
879 fprintf(stderr, " - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi.duplicateIndex() + i), list[i].clsOffset, list[i].hiOffset);
880 }
881 }
882 }
883 else if ( options.mode == modeObjCClasses ) {
884 using dyld3::json::Node;
885 using ObjCClassInfo = dyld3::MachOAnalyzer::ObjCClassInfo;
886 const bool rebased = false;
887
888 // Build a map of class vm addrs to their names so that categories know the
889 // name of the class they are attaching to
890 __block std::map<uint64_t, const char*> classVMAddrToName;
891 __block std::map<uint64_t, const char*> metaclassVMAddrToName;
892 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
893 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
894 const uint32_t pointerSize = ma->pointerSize();
895
896 auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
897 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
898 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
899 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
900 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
901 if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
902 return;
903
904 if (isMetaClass)
905 metaclassVMAddrToName[classVMAddr] = className;
906 else
907 classVMAddrToName[classVMAddr] = className;
908 };
909
910 Diagnostics diag;
911 ma->forEachObjCClass(diag, rebased, visitClass);
912 });
913
914 // These are used only for the on-disk binaries we analyze
915 __block std::vector<const char*> onDiskChainedFixupBindTargets;
916 __block std::map<uint64_t, const char*> onDiskClassVMAddrToName;
917 __block std::map<uint64_t, const char*> onDiskMetaclassVMAddrToName;
918
919 __block Node root;
920 auto makeNode = [](std::string str) -> Node {
921 Node node;
922 node.value = str;
923 return node;
924 };
925
926 auto getProperties = ^(const dyld3::MachOAnalyzer* ma, uint64_t propertiesVMAddr) {
927 __block Node propertiesNode;
928 auto visitProperty = ^(uint64_t propertyVMAddr, const dyld3::MachOAnalyzer::ObjCProperty& property) {
929 // Get the name
930 dyld3::MachOAnalyzer::PrintableStringResult propertyNameResult;
931 const char* propertyName = ma->getPrintableString(property.nameVMAddr, propertyNameResult);
932 if (propertyNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
933 return;
934
935 // Get the attributes
936 dyld3::MachOAnalyzer::PrintableStringResult propertyAttributesResult;
937 const char* propertyAttributes = ma->getPrintableString(property.attributesVMAddr, propertyAttributesResult);
938 if (propertyAttributesResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
939 return;
940
941 Node propertyNode;
942 propertyNode.map["name"] = makeNode(propertyName);
943 propertyNode.map["attributes"] = makeNode(propertyAttributes);
944 propertiesNode.array.push_back(propertyNode);
945 };
946 ma->forEachObjCProperty(propertiesVMAddr, rebased, visitProperty);
947 return propertiesNode.array.empty() ? std::optional<Node>() : propertiesNode;
948 };
949
950 auto getClasses = ^(const dyld3::MachOAnalyzer* ma) {
951 Diagnostics diag;
952 const uint32_t pointerSize = ma->pointerSize();
953
954 __block Node classesNode;
955 __block bool skippedPreviousClass = false;
956 auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
957 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
958 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
959 if (isMetaClass) {
960 if (skippedPreviousClass) {
961 // If the class was bad, then skip the meta class too
962 skippedPreviousClass = false;
963 return;
964 }
965 } else {
966 skippedPreviousClass = true;
967 }
968
969 std::string classType = "-";
970 if (isMetaClass)
971 classType = "+";
972 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
973 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
974 if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) {
975 return;
976 }
977
978 const char* superClassName = nullptr;
979 if ( ma->inDyldCache() ) {
980 if ( objcClass.superclassVMAddr != 0 ) {
981 if (isMetaClass) {
982 // If we are root class, then our superclass should actually point to our own class
983 const uint32_t RO_ROOT = (1<<1);
984 if ( objcClass.flags(pointerSize) & RO_ROOT ) {
985 auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
986 assert(it != classVMAddrToName.end());
987 superClassName = it->second;
988 } else {
989 auto it = metaclassVMAddrToName.find(objcClass.superclassVMAddr);
990 assert(it != metaclassVMAddrToName.end());
991 superClassName = it->second;
992 }
993 } else {
994 auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
995 assert(it != classVMAddrToName.end());
996 superClassName = it->second;
997 }
998 }
999 } else {
1000 // On-disk binary. Lets crack the chain to work out what we are pointing at
1001 dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
1002 fixup.raw64 = objcClass.superclassVMAddr;
1003 assert(!fixup.arm64e.authBind.auth);
1004 if (fixup.arm64e.bind.bind) {
1005 // Bind to another image. Use the bind table to work out which name to bind to
1006 const char* symbolName = onDiskChainedFixupBindTargets[fixup.arm64e.bind.ordinal];
1007 if (isMetaClass) {
1008 if ( strstr(symbolName, "_OBJC_METACLASS_$_") == symbolName ) {
1009 superClassName = symbolName + strlen("_OBJC_METACLASS_$_");
1010 } else {
1011 // Swift classes don't start with these prefixes so just skip them
1012 if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
1013 return;
1014 }
1015 } else {
1016 if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
1017 superClassName = symbolName + strlen("_OBJC_CLASS_$_");
1018 } else {
1019 // Swift classes don't start with these prefixes so just skip them
1020 if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
1021 return;
1022 }
1023 }
1024 } else {
1025 // Rebase within this image.
1026 if (isMetaClass) {
1027 auto it = onDiskMetaclassVMAddrToName.find(objcClass.superclassVMAddr);
1028 assert(it != onDiskMetaclassVMAddrToName.end());
1029 superClassName = it->second;
1030 } else {
1031 auto it = onDiskClassVMAddrToName.find(objcClass.superclassVMAddr);
1032 assert(it != onDiskClassVMAddrToName.end());
1033 superClassName = it->second;
1034 }
1035 }
1036 }
1037
1038 // Print the methods on this class
1039 __block Node methodsNode;
1040 auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1041 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
1042 const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
1043 if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
1044 return;
1045 methodsNode.array.push_back(makeNode(classType + methodName));
1046 };
1047 ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), rebased,
1048 visitMethod);
1049
1050 std::optional<Node> properties = getProperties(ma, objcClass.basePropertiesVMAddr(pointerSize));
1051
1052 if (isMetaClass) {
1053 assert(!classesNode.array.empty());
1054 Node& currentClassNode = classesNode.array.back();
1055 assert(currentClassNode.map["className"].value == className);
1056 if (!methodsNode.array.empty()) {
1057 Node& currentMethodsNode = currentClassNode.map["methods"];
1058 currentMethodsNode.array.insert(currentMethodsNode.array.end(),
1059 methodsNode.array.begin(),
1060 methodsNode.array.end());
1061 }
1062 if (properties.has_value()) {
1063 Node& currentPropertiesNode = currentClassNode.map["properties"];
1064 currentPropertiesNode.array.insert(currentPropertiesNode.array.end(),
1065 properties->array.begin(),
1066 properties->array.end());
1067 }
1068 return;
1069 }
1070
1071 Node currentClassNode;
1072 currentClassNode.map["className"] = makeNode(className);
1073 if ( superClassName != nullptr )
1074 currentClassNode.map["superClassName"] = makeNode(superClassName);
1075 if (!methodsNode.array.empty())
1076 currentClassNode.map["methods"] = methodsNode;
1077 if (properties.has_value())
1078 currentClassNode.map["properties"] = properties.value();
1079
1080 // We didn't skip this class so mark it as such
1081 skippedPreviousClass = false;
1082
1083 classesNode.array.push_back(currentClassNode);
1084 };
1085
1086 ma->forEachObjCClass(diag, rebased, visitClass);
1087 return classesNode.array.empty() ? std::optional<Node>() : classesNode;
1088 };
1089
1090 auto getCategories = ^(const dyld3::MachOAnalyzer* ma) {
1091 Diagnostics diag;
1092
1093 __block Node categoriesNode;
1094 auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
1095 const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
1096 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
1097 const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
1098 if (categoryNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
1099 return;
1100
1101 const char* className = nullptr;
1102 if ( ma->inDyldCache() ) {
1103 auto it = classVMAddrToName.find(objcCategory.clsVMAddr);
1104 assert(it != classVMAddrToName.end());
1105 className = it->second;
1106 } else {
1107 // On-disk binary. Lets crack the chain to work out what we are pointing at
1108 dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
1109 fixup.raw64 = objcCategory.clsVMAddr;
1110 assert(!fixup.arm64e.authBind.auth);
1111 if (fixup.arm64e.bind.bind) {
1112 // Bind to another image. Use the bind table to work out which name to bind to
1113 const char* symbolName = onDiskChainedFixupBindTargets[fixup.arm64e.bind.ordinal];
1114 if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
1115 className = symbolName + strlen("_OBJC_CLASS_$_");
1116 } else {
1117 // Swift classes don't start with these prefixes so just skip them
1118 // We don't know that this is a Swift class/category though, but skip it anyway
1119 return;
1120 }
1121 } else {
1122 auto it = onDiskClassVMAddrToName.find(objcCategory.clsVMAddr);
1123 if (it == onDiskClassVMAddrToName.end()) {
1124 // This is an odd binary with perhaps a Swift class. Just skip this entry
1125 return;
1126 }
1127 className = it->second;
1128 }
1129 }
1130
1131 // Print the instance methods on this category
1132 __block Node methodsNode;
1133 auto visitInstanceMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1134 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
1135 const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
1136 if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
1137 return;
1138 methodsNode.array.push_back(makeNode(std::string("-") + methodName));
1139 };
1140 ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, rebased,
1141 visitInstanceMethod);
1142
1143 // Print the instance methods on this category
1144 __block Node classMethodsNode;
1145 auto visitClassMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1146 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
1147 const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
1148 if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
1149 return;
1150 methodsNode.array.push_back(makeNode(std::string("+") + methodName));
1151 };
1152 ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, rebased,
1153 visitClassMethod);
1154
1155 Node currentCategoryNode;
1156 currentCategoryNode.map["categoryName"] = makeNode(categoryName);
1157 currentCategoryNode.map["className"] = makeNode(className);
1158 if (!methodsNode.array.empty())
1159 currentCategoryNode.map["methods"] = methodsNode;
1160 if (std::optional<Node> properties = getProperties(ma, objcCategory.instancePropertiesVMAddr))
1161 currentCategoryNode.map["properties"] = properties.value();
1162
1163 categoriesNode.array.push_back(currentCategoryNode);
1164 };
1165
1166 ma->forEachObjCCategory(diag, rebased, visitCategory);
1167 return categoriesNode.array.empty() ? std::optional<Node>() : categoriesNode;
1168 };
1169
1170 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
1171 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
1172
1173 Node imageRecord;
1174 imageRecord.map["imagePath"] = makeNode(installName);
1175 imageRecord.map["imageType"] = makeNode("cache-dylib");
1176 std::optional<Node> classes = getClasses(ma);
1177 std::optional<Node> categories = getCategories(ma);
1178
1179 // Skip emitting images with no objc data
1180 if (!classes.has_value() && !categories.has_value())
1181 return;
1182 if (classes.has_value())
1183 imageRecord.map["classes"] = classes.value();
1184 if (categories.has_value())
1185 imageRecord.map["categories"] = categories.value();
1186
1187 root.array.push_back(imageRecord);
1188 });
1189
1190 FileSystemPhysical fileSystem;
1191 dyld3::Platform platform = dyldCache->platform();
1192 const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(dyldCache->archName(), true);
1193
1194 dyldCache->forEachLaunchClosure(^(const char *executableRuntimePath, const dyld3::closure::LaunchClosure *closure) {
1195 Diagnostics diag;
1196 char realerPath[MAXPATHLEN];
1197 dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, executableRuntimePath, archs, platform, realerPath);
1198 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
1199 uint32_t pointerSize = ma->pointerSize();
1200
1201 // Populate the bind targets for classes from other images
1202 onDiskChainedFixupBindTargets.clear();
1203 ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
1204 onDiskChainedFixupBindTargets.push_back(symbolName);
1205 });
1206 if ( diag.hasError() )
1207 return;
1208
1209 // Populate the rebase targets for class names
1210 onDiskMetaclassVMAddrToName.clear();
1211 onDiskClassVMAddrToName.clear();
1212 auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
1213 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
1214 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
1215 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
1216 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
1217 if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
1218 return;
1219
1220 if (isMetaClass)
1221 onDiskMetaclassVMAddrToName[classVMAddr] = className;
1222 else
1223 onDiskClassVMAddrToName[classVMAddr] = className;
1224 };
1225
1226 ma->forEachObjCClass(diag, rebased, visitClass);
1227
1228 Node imageRecord;
1229 imageRecord.map["imagePath"] = makeNode(executableRuntimePath);
1230 imageRecord.map["imageType"] = makeNode("executable");
1231 std::optional<Node> classes = getClasses(ma);
1232 std::optional<Node> categories = getCategories(ma);
1233
1234 // Skip emitting images with no objc data
1235 if (!classes.has_value() && !categories.has_value())
1236 return;
1237 if (classes.has_value())
1238 imageRecord.map["classes"] = classes.value();
1239 if (categories.has_value())
1240 imageRecord.map["categories"] = categories.value();
1241
1242 root.array.push_back(imageRecord);
1243 });
1244
1245 dyldCache->forEachDlopenImage(^(const char *runtimePath, const dyld3::closure::Image *image) {
1246 Diagnostics diag;
1247 char realerPath[MAXPATHLEN];
1248 dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath, archs, platform, realerPath);
1249 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
1250 uint32_t pointerSize = ma->pointerSize();
1251
1252 // Populate the bind targets for classes from other images
1253 onDiskChainedFixupBindTargets.clear();
1254 ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
1255 onDiskChainedFixupBindTargets.push_back(symbolName);
1256 });
1257 if ( diag.hasError() )
1258 return;
1259
1260 // Populate the rebase targets for class names
1261 onDiskMetaclassVMAddrToName.clear();
1262 onDiskClassVMAddrToName.clear();
1263 auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
1264 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
1265 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
1266 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
1267 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
1268 if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
1269 return;
1270
1271 if (isMetaClass)
1272 onDiskMetaclassVMAddrToName[classVMAddr] = className;
1273 else
1274 onDiskClassVMAddrToName[classVMAddr] = className;
1275 };
1276
1277 ma->forEachObjCClass(diag, rebased, visitClass);
1278
1279 Node imageRecord;
1280 imageRecord.map["imagePath"] = makeNode(runtimePath);
1281 imageRecord.map["imageType"] = makeNode("non-cache-dylib");
1282 std::optional<Node> classes = getClasses(ma);
1283 std::optional<Node> categories = getCategories(ma);
1284
1285 // Skip emitting images with no objc data
1286 if (!classes.has_value() && !categories.has_value())
1287 return;
1288 if (classes.has_value())
1289 imageRecord.map["classes"] = classes.value();
1290 if (categories.has_value())
1291 imageRecord.map["categories"] = categories.value();
1292
1293 root.array.push_back(imageRecord);
1294 });
1295
1296 dyld3::json::printJSON(root, 0, std::cout);
1297 }
1298 else if ( options.mode == modeObjCSelectors ) {
1299 if ( dyldCache->objcOpt() == nullptr ) {
1300 fprintf(stderr, "Error: could not get optimized objc\n");
1301 return 1;
1302 }
1303 const objc_opt::objc_selopt_t* selectors = dyldCache->objcOpt()->selopt();
1304 if ( selectors == nullptr ) {
1305 fprintf(stderr, "Error: could not get optimized objc selectors\n");
1306 return 1;
1307 }
1308
1309 std::vector<const char*> selNames;
1310 for (uint64_t index = 0; index != selectors->capacity; ++index) {
1311 objc_opt::objc_stringhash_offset_t offset = selectors->offsets()[index];
1312 if ( offset == 0 )
1313 continue;
1314 const char* selName = selectors->getEntryForIndex((uint32_t)index);
1315 selNames.push_back(selName);
1316 }
1317
1318 std::sort(selNames.begin(), selNames.end(),
1319 [](const char* a, const char* b) {
1320 // Sort by offset, not string value
1321 return a < b;
1322 });
1323
1324 auto makeNode = [](std::string str) {
1325 dyld3::json::Node node;
1326 node.value = str;
1327 return node;
1328 };
1329
1330 dyld3::json::Node root;
1331 for (const char* selName : selNames) {
1332 dyld3::json::Node selNode;
1333 selNode.map["selectorName"] = makeNode(selName);
1334 selNode.map["offset"] = makeNode(dyld3::json::decimal((uint64_t)selName - (uint64_t)dyldCache));
1335
1336 root.array.push_back(selNode);
1337 }
1338
1339 dyld3::json::printJSON(root, 0, std::cout);
1340 }
1341 else if ( options.mode == modeExtract ) {
1342 char pathBuffer[PATH_MAX];
1343 uint32_t bufferSize = PATH_MAX;
1344 if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) {
1345 fprintf(stderr, "Error: could not get path of program\n");
1346 return 1;
1347 }
1348 char* last = strrchr(pathBuffer, '/');
1349 // The bundle is at a different location on device. Its /usr/lib/dsc_extractor.bundle in the SDK
1350 // but /usr/local/lib/dsc_extractor.bundle on device.
1351 strcpy(last+1, DSC_BUNDLE_REL_PATH);
1352 void* handle = dlopen(pathBuffer, RTLD_LAZY);
1353 if ( handle == NULL ) {
1354 fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer);
1355 return 1;
1356 }
1357
1358 typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
1359 void (^progress)(unsigned current, unsigned total));
1360
1361 extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
1362 if ( proc == NULL ) {
1363 fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
1364 return 1;
1365 }
1366
1367 int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } );
1368 return result;
1369 }
1370 else {
1371 switch ( options.mode ) {
1372 case modeList: {
1373 if (options.printInodes) {
1374 dyldCache->forEachImageEntry(^(const char* path, uint64_t mTime, uint64_t inode) {
1375 printf("0x%08llX 0x%08llX ", inode, mTime);
1376 if ( isAlias(path, dyldCache) )
1377 printf("[alias] %s\n", path);
1378 else
1379 printf("%s\n", path);
1380 });
1381 } else {
1382 dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char *dylibUUID, const char *installName, bool &stop) {
1383 if ( options.printVMAddrs )
1384 printf("0x%08llX ", loadAddressUnslid);
1385 if ( options.printUUIDs ) {
1386 const uint8_t* uuid = (uint8_t*)dylibUUID;
1387 printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ",
1388 uuid[0], uuid[1], uuid[2], uuid[3],
1389 uuid[4], uuid[5], uuid[6], uuid[7],
1390 uuid[8], uuid[9], uuid[10], uuid[11],
1391 uuid[12], uuid[13], uuid[14], uuid[15]);
1392 }
1393 if ( isAlias(installName, dyldCache) )
1394 printf("[alias] %s\n", installName);
1395 else
1396 printf("%s\n", installName);
1397 });
1398 }
1399 break;
1400 }
1401 case modeMap: {
1402 __block std::map<uint64_t, const char*> dataSegNames;
1403 __block std::map<uint64_t, uint64_t> dataSegEnds;
1404 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1405 dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
1406 mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
1407 if ( isAlias(installName, dyldCache) )
1408 return;
1409 printf("0x%08llX - 0x%08llX %s %s\n", info.vmAddr, info.vmAddr + info.vmSize, info.segName, installName);
1410 if ( strncmp(info.segName, "__DATA", 6) == 0 ) {
1411 dataSegNames[info.vmAddr] = installName;
1412 dataSegEnds[info.vmAddr] = info.vmAddr + info.vmSize;
1413 }
1414 });
1415 });
1416 // <rdar://problem/51084507> Enhance dyld_shared_cache_util to show where section alignment added padding
1417 uint64_t lastEnd = 0;
1418 for (const auto& entry : dataSegEnds) {
1419 uint64_t padding = entry.first - lastEnd;
1420 if ( (padding > 32) && (lastEnd != 0) ) {
1421 printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd, entry.first, padding/1024);
1422 }
1423 lastEnd = entry.second;
1424 }
1425 break;
1426 }
1427 case modeDependencies: {
1428 __block bool dependentTargetFound = false;
1429 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1430 if ( strcmp(options.dependentsOfPath, installName) != 0 )
1431 return;
1432 dependentTargetFound = true;
1433
1434 auto printDep = [&options](const char *loadPath, uint32_t compatVersion, uint32_t curVersion) {
1435 if ( options.printDylibVersions ) {
1436 uint32_t compat_vers = compatVersion;
1437 uint32_t current_vers = curVersion;
1438 printf("\t%s", loadPath);
1439 if ( compat_vers != 0xFFFFFFFF ) {
1440 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
1441 (compat_vers >> 16),
1442 (compat_vers >> 8) & 0xff,
1443 (compat_vers) & 0xff,
1444 (current_vers >> 16),
1445 (current_vers >> 8) & 0xff,
1446 (current_vers) & 0xff);
1447 }
1448 else {
1449 printf("\n");
1450 }
1451 }
1452 else {
1453 printf("\t%s\n", loadPath);
1454 }
1455 };
1456
1457 dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
1458
1459 // First print out our dylib and version.
1460 const char* dylibInstallName;
1461 uint32_t currentVersion;
1462 uint32_t compatVersion;
1463 if ( mf->getDylibInstallName(&dylibInstallName, &compatVersion, &currentVersion) ) {
1464 printDep(dylibInstallName, compatVersion, currentVersion);
1465 }
1466
1467 // Then the dependent dylibs.
1468 mf->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
1469 printDep(loadPath, compatVersion, curVersion);
1470 });
1471 });
1472 if (options.dependentsOfPath && !dependentTargetFound) {
1473 fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath);
1474 exit(1);
1475 }
1476 break;
1477 }
1478 case modeLinkEdit: {
1479 std::map<uint32_t, const char*> pageToContent;
1480 auto add_linkedit = [&pageToContent](uint32_t pageStart, uint32_t pageEnd, const char* message) {
1481 for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
1482 std::map<uint32_t, const char*>::iterator pos = pageToContent.find(p);
1483 if ( pos == pageToContent.end() ) {
1484 pageToContent[p] = strdup(message);
1485 }
1486 else {
1487 const char* oldMessage = pos->second;
1488 char* newMesssage;
1489 asprintf(&newMesssage, "%s, %s", oldMessage, message);
1490 pageToContent[p] = newMesssage;
1491 ::free((void*)oldMessage);
1492 }
1493 }
1494 };
1495
1496 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1497 // Filter out symlinks.
1498 if (isAlias(installName, dyldCache))
1499 return;
1500 dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1501 Diagnostics diag;
1502 dyld3::MachOAnalyzer::LinkEditInfo leInfo;
1503 ma->getLinkEditPointers(diag, leInfo);
1504
1505 if (diag.hasError())
1506 return;
1507
1508 char message[1000];
1509 const char* shortName = strrchr(installName, '/') + 1;
1510 // add export trie info
1511 if ( leInfo.dyldInfo->export_size != 0 ) {
1512 //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off());
1513 uint32_t exportPageOffsetStart = leInfo.dyldInfo->export_off & (-4096);
1514 uint32_t exportPageOffsetEnd = (leInfo.dyldInfo->export_off + leInfo.dyldInfo->export_size) & (-4096);
1515 sprintf(message, "exports from %s", shortName);
1516 add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message);
1517 }
1518 // add binding info
1519 if ( leInfo.dyldInfo->bind_size != 0 ) {
1520 uint32_t bindPageOffsetStart = leInfo.dyldInfo->bind_off & (-4096);
1521 uint32_t bindPageOffsetEnd = (leInfo.dyldInfo->bind_off + leInfo.dyldInfo->bind_size) & (-4096);
1522 sprintf(message, "bindings from %s", shortName);
1523 add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message);
1524 }
1525 // add lazy binding info
1526 if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
1527 uint32_t lazybindPageOffsetStart = leInfo.dyldInfo->lazy_bind_off & (-4096);
1528 uint32_t lazybindPageOffsetEnd = (leInfo.dyldInfo->lazy_bind_off + leInfo.dyldInfo->lazy_bind_size) & (-4096);
1529 sprintf(message, "lazy bindings from %s", shortName);
1530 add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message);
1531 }
1532 // add weak binding info
1533 if ( leInfo.dyldInfo->weak_bind_size != 0 ) {
1534 uint32_t weakbindPageOffsetStart = leInfo.dyldInfo->weak_bind_off & (-4096);
1535 uint32_t weakbindPageOffsetEnd = (leInfo.dyldInfo->weak_bind_off + leInfo.dyldInfo->weak_bind_size) & (-4096);
1536 sprintf(message, "weak bindings from %s", shortName);
1537 add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message);
1538 }
1539 });
1540
1541 for (std::map<uint32_t, const char*>::iterator it = pageToContent.begin(); it != pageToContent.end(); ++it) {
1542 printf("0x%08X %s\n", it->first, it->second);
1543 }
1544 break;
1545 }
1546 case modeSize: {
1547 struct TextInfo {
1548 uint64_t textSize;
1549 const char* path;
1550 };
1551 __block std::vector<TextInfo> textSegments;
1552 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1553 // Filter out symlinks.
1554 if (isAlias(installName, dyldCache))
1555 return;
1556
1557 dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1558 ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
1559 if ( strcmp(info.segName, "__TEXT") != 0 )
1560 return;
1561 textSegments.push_back({ info.fileSize, installName });
1562 });
1563 });
1564 std::sort(textSegments.begin(), textSegments.end(), [](const TextInfo& left, const TextInfo& right) {
1565 return (left.textSize > right.textSize);
1566 });
1567 for (std::vector<TextInfo>::iterator it = textSegments.begin(); it != textSegments.end(); ++it) {
1568 printf(" 0x%08llX %s\n", it->textSize, it->path);
1569 }
1570 break;
1571 }
1572 case modeNone:
1573 case modeInfo:
1574 case modeSlideInfo:
1575 case modeVerboseSlideInfo:
1576 case modeAcceleratorInfo:
1577 case modeTextInfo:
1578 case modeLocalSymbols:
1579 case modeJSONMap:
1580 case modeJSONDependents:
1581 case modeSectionSizes:
1582 case modeStrings:
1583 case modeObjCProtocols:
1584 case modeObjCClasses:
1585 case modeObjCSelectors:
1586 case modeExtract:
1587 break;
1588 }
1589 }
1590 return 0;
1591 }
1592