dyld-732.8.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
48 #include "DyldSharedCache.h"
49 #include "Trie.hpp"
50
51 #include "objc-shared-cache.h"
52
53 #if TARGET_OS_OSX
54 #define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle"
55 #else
56 #define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle"
57 #endif
58
59 // mmap() an shared cache file read/only but laid out like it would be at runtime
60 static const DyldSharedCache* mapCacheFile(const char* path)
61 {
62 struct stat statbuf;
63 if ( ::stat(path, &statbuf) ) {
64 fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
65 return nullptr;
66 }
67
68 int cache_fd = ::open(path, O_RDONLY);
69 if (cache_fd < 0) {
70 fprintf(stderr, "Error: failed to open shared cache file at %s\n", path);
71 return nullptr;
72 }
73
74 uint8_t firstPage[4096];
75 if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
76 fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
77 return nullptr;
78 }
79 const dyld_cache_header* header = (dyld_cache_header*)firstPage;
80 const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
81
82 size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
83 vm_address_t result;
84 kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
85 if ( r != KERN_SUCCESS ) {
86 fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
87 return nullptr;
88 }
89 for (int i=0; i < 3; ++i) {
90 void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
91 PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
92 if (mapped_cache == MAP_FAILED) {
93 fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", path, errno);
94 return nullptr;
95 }
96 }
97 ::close(cache_fd);
98
99 return (DyldSharedCache*)result;
100 }
101
102 enum Mode {
103 modeNone,
104 modeList,
105 modeMap,
106 modeDependencies,
107 modeSlideInfo,
108 modeVerboseSlideInfo,
109 modeAcceleratorInfo,
110 modeTextInfo,
111 modeLinkEdit,
112 modeLocalSymbols,
113 modeJSONMap,
114 modeJSONDependents,
115 modeSectionSizes,
116 modeStrings,
117 modeInfo,
118 modeSize,
119 modeObjCProtocols,
120 modeExtract
121 };
122
123 struct Options {
124 Mode mode;
125 const char* dependentsOfPath;
126 const char* extractionDir;
127 bool printUUIDs;
128 bool printVMAddrs;
129 bool printDylibVersions;
130 bool printInodes;
131 };
132
133
134 void usage() {
135 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");
136 }
137
138 static void checkMode(Mode mode) {
139 if ( mode != modeNone ) {
140 fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n");
141 usage();
142 exit(1);
143 }
144 }
145
146 static bool isAlias(const char* path, const DyldSharedCache* dyldCache) {
147 const dyld_cache_header* header = &dyldCache->header;
148 const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
149 const dyld_cache_mapping_info* textMapping = &mappings[0];
150 // paths for aliases are store between cache header and first segment
151 return path < (char*)textMapping;
152 }
153
154 int main (int argc, const char* argv[]) {
155
156 const char* sharedCachePath = nullptr;
157
158 Options options;
159 options.mode = modeNone;
160 options.printUUIDs = false;
161 options.printVMAddrs = false;
162 options.printDylibVersions = false;
163 options.printInodes = false;
164 options.dependentsOfPath = NULL;
165 options.extractionDir = NULL;
166
167 bool printStrings = false;
168 bool printExports = false;
169
170 for (uint32_t i = 1; i < argc; i++) {
171 const char* opt = argv[i];
172 if (opt[0] == '-') {
173 if (strcmp(opt, "-list") == 0) {
174 checkMode(options.mode);
175 options.mode = modeList;
176 }
177 else if (strcmp(opt, "-dependents") == 0) {
178 checkMode(options.mode);
179 options.mode = modeDependencies;
180 options.dependentsOfPath = argv[++i];
181 if ( i >= argc ) {
182 fprintf(stderr, "Error: option -depdendents requires an argument\n");
183 usage();
184 exit(1);
185 }
186 }
187 else if (strcmp(opt, "-linkedit") == 0) {
188 checkMode(options.mode);
189 options.mode = modeLinkEdit;
190 }
191 else if (strcmp(opt, "-info") == 0) {
192 checkMode(options.mode);
193 options.mode = modeInfo;
194 }
195 else if (strcmp(opt, "-slide_info") == 0) {
196 checkMode(options.mode);
197 options.mode = modeSlideInfo;
198 }
199 else if (strcmp(opt, "-verbose_slide_info") == 0) {
200 checkMode(options.mode);
201 options.mode = modeVerboseSlideInfo;
202 }
203 else if (strcmp(opt, "-accelerator_info") == 0) {
204 checkMode(options.mode);
205 options.mode = modeAcceleratorInfo;
206 }
207 else if (strcmp(opt, "-text_info") == 0) {
208 checkMode(options.mode);
209 options.mode = modeTextInfo;
210 }
211 else if (strcmp(opt, "-local_symbols") == 0) {
212 checkMode(options.mode);
213 options.mode = modeLocalSymbols;
214 }
215 else if (strcmp(opt, "-strings") == 0) {
216 if (options.mode != modeStrings)
217 checkMode(options.mode);
218 options.mode = modeStrings;
219 printStrings = true;
220 }
221 else if (strcmp(opt, "-sections") == 0) {
222 checkMode(options.mode);
223 options.mode = modeSectionSizes;
224 }
225 else if (strcmp(opt, "-exports") == 0) {
226 if (options.mode != modeStrings)
227 checkMode(options.mode);
228 options.mode = modeStrings;
229 printExports = true;
230 }
231 else if (strcmp(opt, "-map") == 0) {
232 checkMode(options.mode);
233 options.mode = modeMap;
234 }
235 else if (strcmp(opt, "-json-map") == 0) {
236 checkMode(options.mode);
237 options.mode = modeJSONMap;
238 }
239 else if (strcmp(opt, "-json-dependents") == 0) {
240 checkMode(options.mode);
241 options.mode = modeJSONDependents;
242 }
243 else if (strcmp(opt, "-size") == 0) {
244 checkMode(options.mode);
245 options.mode = modeSize;
246 }
247 else if (strcmp(opt, "-objc-protocols") == 0) {
248 checkMode(options.mode);
249 options.mode = modeObjCProtocols;
250 }
251 else if (strcmp(opt, "-extract") == 0) {
252 checkMode(options.mode);
253 options.mode = modeExtract;
254 options.extractionDir = argv[++i];
255 if ( i >= argc ) {
256 fprintf(stderr, "Error: option -extract requires a directory argument\n");
257 usage();
258 exit(1);
259 }
260 }
261 else if (strcmp(opt, "-uuid") == 0) {
262 options.printUUIDs = true;
263 }
264 else if (strcmp(opt, "-inode") == 0) {
265 options.printInodes = true;
266 }
267 else if (strcmp(opt, "-versions") == 0) {
268 options.printDylibVersions = true;
269 }
270 else if (strcmp(opt, "-vmaddr") == 0) {
271 options.printVMAddrs = true;
272 }
273 else {
274 fprintf(stderr, "Error: unrecognized option %s\n", opt);
275 usage();
276 exit(1);
277 }
278 }
279 else {
280 sharedCachePath = opt;
281 }
282 }
283
284 if ( options.mode == modeNone ) {
285 fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
286 usage();
287 exit(1);
288 }
289
290 if ( options.mode != modeSlideInfo && options.mode != modeVerboseSlideInfo ) {
291 if ( options.printUUIDs && (options.mode != modeList) )
292 fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
293
294 if ( options.printVMAddrs && (options.mode != modeList) )
295 fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
296
297 if ( options.printDylibVersions && (options.mode != modeDependencies) )
298 fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
299
300 if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) {
301 fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
302 usage();
303 exit(1);
304 }
305 }
306
307 const DyldSharedCache* dyldCache = nullptr;
308 if ( sharedCachePath != nullptr ) {
309 dyldCache = mapCacheFile(sharedCachePath);
310 // mapCacheFile prints an error if something goes wrong, so just return in that case.
311 if ( dyldCache == nullptr )
312 return 1;
313 }
314 else {
315 #if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
316 size_t cacheLength;
317 dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
318 #endif
319 if (dyldCache == nullptr) {
320 fprintf(stderr, "Could not get in-memory shared cache\n");
321 return 1;
322 }
323 }
324
325 if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) {
326 const dyld_cache_header* header = &dyldCache->header;
327 if ( header->slideInfoOffset == 0 ) {
328 fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
329 exit(1);
330 }
331 const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
332 const dyld_cache_mapping_info* dataMapping = &mappings[1];
333 uint64_t dataStartAddress = dataMapping->address;
334 uint64_t dataSize = dataMapping->size;
335
336 const dyld_cache_slide_info* slideInfoHeader = dyldCache->slideInfo();
337 printf("slide info version=%d\n", slideInfoHeader->version);
338 if ( slideInfoHeader->version == 1 ) {
339 printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count, dataSize/4096);
340 const dyld_cache_slide_info_entry* entries = (dyld_cache_slide_info_entry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset);
341 const uint16_t* tocs = (uint16_t*)((char*)slideInfoHeader + slideInfoHeader->toc_offset);
342 for(int i=0; i < slideInfoHeader->toc_count; ++i) {
343 printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, tocs[i]);
344 const dyld_cache_slide_info_entry* entry = &entries[tocs[i]];
345 for(int j=0; j < slideInfoHeader->entries_size; ++j)
346 printf("%02X", entry->bits[j]);
347 printf("\n");
348 }
349 }
350 else if ( slideInfoHeader->version == 2 ) {
351 const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(slideInfoHeader);
352 printf("page_size=%d\n", slideInfo->page_size);
353 printf("delta_mask=0x%016llX\n", slideInfo->delta_mask);
354 printf("value_add=0x%016llX\n", slideInfo->value_add);
355 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count, slideInfo->page_extras_count);
356 const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset);
357 const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset);
358 for (int i=0; i < slideInfo->page_starts_count; ++i) {
359 const uint16_t start = starts[i];
360 auto rebaseChain = [&](uint8_t* pageContent, uint16_t startOffset)
361 {
362 uintptr_t slideAmount = 0;
363 const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask);
364 const uintptr_t valueMask = ~deltaMask;
365 const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add);
366 const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
367
368 uint32_t pageOffset = startOffset;
369 uint32_t delta = 1;
370 while ( delta != 0 ) {
371 uint8_t* loc = pageContent + pageOffset;
372 uintptr_t rawValue = *((uintptr_t*)loc);
373 delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
374 uintptr_t value = (rawValue & valueMask);
375 if ( value != 0 ) {
376 value += valueAdd;
377 value += slideAmount;
378 }
379 printf(" [% 5d + 0x%04llX]: 0x%016llX = 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue, (uint64_t)value);
380 pageOffset += delta;
381 }
382 };
383 const uint8_t* dataPagesStart = dyldCache->dataRegionStart();
384 if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
385 printf("page[% 5d]: no rebasing\n", i);
386 }
387 else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
388 printf("page[% 5d]: ", i);
389 int j=(start & 0x3FFF);
390 bool done = false;
391 do {
392 uint16_t aStart = extras[j];
393 printf("start=0x%04X ", aStart & 0x3FFF);
394 if ( options.mode == modeVerboseSlideInfo ) {
395 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
396 uint16_t pageStartOffset = (aStart & 0x3FFF)*4;
397 rebaseChain(page, pageStartOffset);
398 }
399 done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
400 ++j;
401 } while ( !done );
402 printf("\n");
403 }
404 else {
405 printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
406 if ( options.mode == modeVerboseSlideInfo ) {
407 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
408 uint16_t pageStartOffset = start*4;
409 rebaseChain(page, pageStartOffset);
410 }
411 }
412 }
413 }
414 else if ( slideInfoHeader->version == 3 ) {
415 const dyld_cache_slide_info3* slideInfo = (dyld_cache_slide_info3*)(slideInfoHeader);
416 printf("page_size=%d\n", slideInfo->page_size);
417 printf("page_starts_count=%d\n", slideInfo->page_starts_count);
418 printf("auth_value_add=0x%016llX\n", slideInfo->auth_value_add);
419 const uintptr_t authValueAdd = (uintptr_t)(slideInfo->auth_value_add);
420 const uint8_t* dataSegmentStart = dyldCache->dataRegionStart();
421 for (int i=0; i < slideInfo->page_starts_count; ++i) {
422 uint16_t delta = slideInfo->page_starts[i];
423 if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
424 printf("page[% 5d]: no rebasing\n", i);
425 continue;
426 }
427
428 printf("page[% 5d]: start=0x%04X\n", i, delta);
429 if ( options.mode != modeVerboseSlideInfo )
430 continue;
431
432 delta = delta/sizeof(uint64_t); // initial offset is byte based
433 const uint8_t* pageStart = dataSegmentStart + (i * slideInfo->page_size);
434 const dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)pageStart;
435 do {
436 loc += delta;
437 delta = loc->plain.offsetToNextPointer;
438 dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
439 ptr.raw64 = *((uint64_t*)loc);
440 if ( loc->auth.authenticated ) {
441 uint64_t target = authValueAdd + loc->auth.offsetFromSharedCacheBase;
442 uint64_t targetValue = ptr.arm64e.signPointer((void*)loc, target);
443 printf(" [% 5d + 0x%04llX]: 0x%016llX (JOP: diversity %d, address %s, %s)\n",
444 i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue,
445 ptr.arm64e.authBind.diversity, ptr.arm64e.authBind.addrDiv ? "true" : "false",
446 ptr.arm64e.keyName());
447 }
448 else {
449 uint64_t targetValue = ptr.arm64e.unpackTarget();
450 printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue);
451 }
452 } while (delta != 0);
453 }
454 }
455 else if ( slideInfoHeader->version == 4 ) {
456 const dyld_cache_slide_info4* slideInfo = (dyld_cache_slide_info4*)(slideInfoHeader);
457 printf("page_size=%d\n", slideInfo->page_size);
458 printf("delta_mask=0x%016llX\n", slideInfo->delta_mask);
459 printf("value_add=0x%016llX\n", slideInfo->value_add);
460 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count, slideInfo->page_extras_count);
461 const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset);
462 const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset);
463 for (int i=0; i < slideInfo->page_starts_count; ++i) {
464 const uint16_t start = starts[i];
465 auto rebaseChainV4 = [&](uint8_t* pageContent, uint16_t startOffset)
466 {
467 uintptr_t slideAmount = 0;
468 const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask);
469 const uintptr_t valueMask = ~deltaMask;
470 const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add);
471 const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
472
473 uint32_t pageOffset = startOffset;
474 uint32_t delta = 1;
475 while ( delta != 0 ) {
476 uint8_t* loc = pageContent + pageOffset;
477 uint32_t rawValue = *((uint32_t*)loc);
478 delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
479 uintptr_t value = (rawValue & valueMask);
480 if ( (value & 0xFFFF8000) == 0 ) {
481 // small positive non-pointer, use as-is
482 }
483 else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
484 // small negative non-pointer
485 value |= 0xC0000000;
486 }
487 else {
488 value += valueAdd;
489 value += slideAmount;
490 }
491 printf(" [% 5d + 0x%04X]: 0x%08X\n", i, pageOffset, rawValue);
492 pageOffset += delta;
493 }
494 };
495 const uint8_t* dataPagesStart = dyldCache->dataRegionStart();
496 if ( start == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
497 printf("page[% 5d]: no rebasing\n", i);
498 }
499 else if ( start & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
500 printf("page[% 5d]: ", i);
501 int j=(start & DYLD_CACHE_SLIDE4_PAGE_INDEX);
502 bool done = false;
503 do {
504 uint16_t aStart = extras[j];
505 printf("start=0x%04X ", aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX);
506 if ( options.mode == modeVerboseSlideInfo ) {
507 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
508 uint16_t pageStartOffset = (aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX)*4;
509 rebaseChainV4(page, pageStartOffset);
510 }
511 done = (extras[j] & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
512 ++j;
513 } while ( !done );
514 printf("\n");
515 }
516 else {
517 printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
518 if ( options.mode == modeVerboseSlideInfo ) {
519 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
520 uint16_t pageStartOffset = start*4;
521 rebaseChainV4(page, pageStartOffset);
522 }
523 }
524 }
525 }
526 }
527 else if ( options.mode == modeInfo ) {
528 const dyld_cache_header* header = &dyldCache->header;
529 printf("uuid: ");
530 if ( header->mappingOffset >= 0x68 ) {
531 const uint8_t* uuid = header->uuid;
532 printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
533 uuid[0], uuid[1], uuid[2], uuid[3],
534 uuid[4], uuid[5], uuid[6], uuid[7],
535 uuid[8], uuid[9], uuid[10], uuid[11],
536 uuid[12], uuid[13], uuid[14], uuid[15]);
537 }
538 else {
539 printf("n/a\n");
540 }
541 if ( header->mappingOffset >= 0xE0 ) {
542 // HACK until this uses new header
543 uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8));
544 uint32_t bitfield = *((uint32_t*)(((char*)header) + 0xDC));
545 uint32_t simulator = bitfield & 0x200;
546 uint32_t locallyBuiltCache = bitfield & 0x400;
547 switch (platform) {
548 case 1:
549 printf("platform: macOS\n");
550 break;
551 case 2:
552 if ( simulator )
553 printf("platform: iOS simulator\n");
554 else
555 printf("platform: iOS\n");
556 break;
557 case 3:
558 if ( simulator )
559 printf("platform: tvOS simulator\n");
560 else
561 printf("platform: tvOS\n");
562 break;
563 case 4:
564 if ( simulator )
565 printf("platform: watchOS simulator\n");
566 else
567 printf("platform: watchOS\n");
568 break;
569 case 5:
570 printf("platform: bridgeOS\n");
571 break;
572 default:
573 printf("platform: 0x%08X 0x%08X\n", platform, simulator);
574 }
575 printf("built by: %s\n", locallyBuiltCache ? "local machine" : "B&I");
576 }
577 printf("cache type: %s\n", header->cacheType ? "production" : "development");
578 printf("image count: %u\n", header->imagesCount);
579 if ( (header->mappingOffset >= 0x78) && (header->branchPoolsOffset != 0) ) {
580 printf("branch pool count: %u\n", header->branchPoolsCount);
581 }
582 if ( header->slideInfoSize > 0 ) {
583 uint32_t pageSize = 0x4000; // fix me for intel
584 uint32_t possibleSlideValues = (uint32_t)(header->maxSlide/pageSize);
585 uint32_t entropyBits = 32 - __builtin_clz(possibleSlideValues - 1);
586 printf("ASLR entropy: %u-bits\n", entropyBits);
587 }
588 printf("mappings:\n");
589 const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
590 for (uint32_t i=0; i < header->mappingCount; ++i) {
591 if ( mappings[i].initProt & VM_PROT_EXECUTE )
592 printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
593 mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
594 mappings[i].address, mappings[i].address + mappings[i].size);
595 else if ( mappings[i].initProt & VM_PROT_WRITE )
596 printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
597 mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
598 mappings[i].address, mappings[i].address + mappings[i].size);
599 else if ( mappings[i].initProt & VM_PROT_READ )
600 printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
601 mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
602 mappings[i].address, mappings[i].address + mappings[i].size);
603 }
604 if ( header->codeSignatureOffset != 0 ) {
605 uint64_t size = header->codeSignatureSize;
606 uint64_t csAddr = mappings[header->mappingCount-1].address + mappings[header->mappingCount-1].size;
607 if ( size != 0 )
608 printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
609 size/(1024*1024), header->codeSignatureOffset, header->codeSignatureOffset + size, csAddr, csAddr + size);
610 }
611 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
612 header->slideInfoSize/1024, header->slideInfoOffset, header->slideInfoOffset + header->slideInfoSize);
613 if ( header->localSymbolsOffset != 0 )
614 printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
615 header->localSymbolsSize/(1024*1024), header->localSymbolsOffset, header->localSymbolsOffset + header->localSymbolsSize);
616 if ( (header->mappingOffset >= 0x78) && (header->accelerateInfoSize != 0) )
617 printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n",
618 header->accelerateInfoSize/1024, header->accelerateInfoAddr, header->accelerateInfoAddr + header->accelerateInfoSize);
619 }
620 else if ( options.mode == modeAcceleratorInfo ) {
621 const dyld_cache_header* header = &dyldCache->header;
622 if ( (header->mappingOffset < sizeof(dyld_cache_header)) || (header->accelerateInfoSize == 0) ) {
623 printf("no accelerator info\n");
624 }
625 else {
626 const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
627 uint64_t aiAddr = header->accelerateInfoAddr;
628 const dyld_cache_accelerator_info* accelInfo = NULL;
629 for (uint32_t i=0; i < header->mappingCount; ++i) {
630 if ( (mappings[i].address <= aiAddr) && (aiAddr < mappings[i].address+mappings[i].size) ) {
631 uint64_t offset = aiAddr - mappings[i].address + mappings[i].fileOffset;
632 accelInfo = (dyld_cache_accelerator_info*)((uint8_t*)dyldCache + offset);
633 }
634 }
635 if ( accelInfo == NULL ) {
636 printf("accelerator info not in any mapped range\n");
637 }
638 else {
639 const dyld_cache_image_info* images = (dyld_cache_image_info*)((char*)dyldCache + header->imagesOffset);
640 const dyld_cache_image_info_extra* imagesExtra = (dyld_cache_image_info_extra*)((char*)accelInfo + accelInfo->imagesExtrasOffset);
641 const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset);
642 const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset);
643 printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount);
644 for (uint32_t i=0; i < accelInfo->imageExtrasCount; ++i) {
645 printf(" image[%3u] %s:\n", i, (char*)dyldCache +images[i].pathFileOffset);
646 printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr, imagesExtra[i].exportsTrieSize);
647 if ( imagesExtra[i].weakBindingsSize )
648 printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr, imagesExtra[i].weakBindingsSize);
649 printf(" dependents: ");
650 for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex; dependencyArray[d] != 0xFFFF; ++d) {
651 uint16_t depIndex = dependencyArray[d];
652 if ( depIndex & 0x8000 )
653 printf(" up(%d) ", depIndex & 0x7FFF);
654 else
655 printf(" %d ", depIndex);
656 }
657 printf("\n");
658 printf(" re-exports: ");
659 for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex; reExportArray[r] != 0xFFFF; ++r)
660 printf(" %d ", reExportArray[r]);
661 printf("\n");
662 }
663 printf("libdyld.dylib:\n");
664 printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr);
665 printf("initializers (count=%u):\n", accelInfo->initializersCount);
666 const dyld_cache_accelerator_initializer* initializers = (dyld_cache_accelerator_initializer*)((char*)accelInfo + accelInfo->initializersOffset);
667 for (uint32_t i=0; i < accelInfo->initializersCount; ++i) {
668 printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex, mappings[0].address + initializers[i].functionOffset);
669 }
670 printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount);
671 const dyld_cache_accelerator_dof* dofs = (dyld_cache_accelerator_dof*)((char*)accelInfo + accelInfo->dofSectionsOffset);
672 for (uint32_t i=0; i < accelInfo->dofSectionsCount; ++i) {
673 printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex, dofs[i].sectionAddress, dofs[i].sectionAddress+dofs[i].sectionSize);
674 }
675 printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount);
676 const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset);
677 for (uint32_t i=0; i < accelInfo->imageExtrasCount; ++i) {
678 unsigned imageIndex = bottomUpArray[i];
679 if ( imageIndex < accelInfo->imageExtrasCount )
680 printf(" image[%3u] %s\n", imageIndex, (char*)dyldCache + images[imageIndex].pathFileOffset);
681 else
682 printf(" image[%3u] BAD INDEX\n", imageIndex);
683 }
684 printf("range table (count=%u):\n", accelInfo->rangeTableCount);
685 const dyld_cache_range_entry* rangeTable = (dyld_cache_range_entry*)((char*)accelInfo + accelInfo->rangeTableOffset);
686 for (uint32_t i=0; i < accelInfo->rangeTableCount; ++i) {
687 const dyld_cache_range_entry& entry = rangeTable[i];
688 printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress, entry.startAddress + entry.size, (char*)dyldCache + images[entry.imageIndex].pathFileOffset);
689 }
690 printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize);
691 const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset;
692 const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize;
693 std::vector<DylibIndexTrie::Entry> dylibEntries;
694 if ( !Trie<DylibIndex>::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) )
695 printf(" malformed dylibs trie\n");
696 for (const DylibIndexTrie::Entry& x : dylibEntries) {
697 printf(" image[%3u] %s\n", x.info.index, x.name.c_str());
698 }
699 }
700 }
701 }
702 else if ( options.mode == modeTextInfo ) {
703 const dyld_cache_header* header = &dyldCache->header;
704 if ( (header->mappingOffset < sizeof(dyld_cache_header)) || (header->imagesTextCount == 0) ) {
705 printf("no text info\n");
706 }
707 else {
708 const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)dyldCache + header->imagesTextOffset);
709 const dyld_cache_image_text_info* imagesTextEnd = &imagesText[header->imagesTextCount];
710 printf("dylib text infos (count=%llu):\n", header->imagesTextCount);
711 for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) {
712 printf(" 0x%09llX -> 0x%09llX <", p->loadAddress, p->loadAddress + p->textSegmentSize);
713 for (int i=0; i<16; ++i) {
714 switch (i) {
715 case 4:
716 case 6:
717 case 8:
718 case 10:
719 printf("-");
720 break;
721 }
722 printf("%02X", p->uuid[i]);
723 }
724 printf("> %s\n", (char*)dyldCache + p->pathOffset);
725 }
726 }
727 }
728 else if ( options.mode == modeLocalSymbols ) {
729 const dyld_cache_header* header = &dyldCache->header;
730 if ( header->localSymbolsOffset == 0 ) {
731 fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
732 exit(1);
733 }
734 const bool is64 = (strstr((char*)dyldCache, "64") != NULL);
735 const dyld_cache_image_info* imageInfos = (dyld_cache_image_info*)((char*)dyldCache + header->imagesOffset);
736 const dyld_cache_local_symbols_info* localsInfo = (dyld_cache_local_symbols_info*)((char*)dyldCache + header->localSymbolsOffset);
737 const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset + localsInfo->nlistOffset);
738 const uint32_t nlistCount = localsInfo->nlistCount;
739 const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12;
740 const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset + localsInfo->stringsOffset);
741 const uint32_t stringsSize = localsInfo->stringsSize;
742 const uint32_t entriesCount = localsInfo->entriesCount;
743 const dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)((char*)localsInfo + localsInfo->entriesOffset);
744 printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize);
745 printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize);
746 printf("local symbols by dylib (count=%d):\n", entriesCount);
747 //const char* stringPool = (char*)dyldCache + stringsFileOffset;
748 for (int i=0; i < entriesCount; ++i) {
749 const char* imageName = (char*)dyldCache + imageInfos[i].pathFileOffset;
750 printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex, entries[i].nlistCount, imageName);
751 #if 0
752 if ( is64 ) {
753 const nlist_64* symTab = (nlist_64*)((char*)dyldCache + nlistFileOffset);
754 for (int e=0; e < entries[i].nlistCount(); ++e) {
755 const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e];
756 printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]);
757 printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value);
758 }
759 }
760 #endif
761 }
762 }
763 else if ( options.mode == modeJSONMap ) {
764 std::string buffer = dyldCache->generateJSONMap("unknown");
765 printf("%s\n", buffer.c_str());
766 }
767 else if ( options.mode == modeJSONDependents ) {
768 std::cout << dyldCache->generateJSONDependents();
769 }
770 else if ( options.mode == modeStrings ) {
771 if (printStrings) {
772 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
773 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
774 int64_t slide = ma->getSlide();
775 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
776 if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
777 if ( malformedSectionRange ) {
778 stop = true;
779 return;
780 }
781 const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
782 const char* s = (char*)content;
783 const char* end = s + info.sectSize;
784 while ( s < end ) {
785 printf("%s: %s\n", ma->installName(), s);
786 while (*s != '\0' )
787 ++s;
788 ++s;
789 }
790 }
791 });
792 });
793 }
794
795 if (printExports) {
796 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
797 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
798 uint32_t exportTrieRuntimeOffset;
799 uint32_t exportTrieSize;
800 if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) {
801 const uint8_t* start = (uint8_t*)mh + exportTrieRuntimeOffset;
802 const uint8_t* end = start + exportTrieSize;
803 std::vector<ExportInfoTrie::Entry> exports;
804 if ( !ExportInfoTrie::parseTrie(start, end, exports) ) {
805 return;
806 }
807
808 for (const ExportInfoTrie::Entry& entry: exports) {
809 printf("%s: %s\n", ma->installName(), entry.name.c_str());
810 }
811 }
812 });
813 }
814 }
815 else if ( options.mode == modeSectionSizes ) {
816 __block std::map<std::string, uint64_t> sectionSizes;
817 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
818 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
819 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
820 std::string section = std::string(sectInfo.segInfo.segName) + " " + sectInfo.sectName;
821 sectionSizes[section] += sectInfo.sectSize;
822 });
823 });
824 for (const auto& keyAndValue : sectionSizes) {
825 printf("%lld %s\n", keyAndValue.second, keyAndValue.first.c_str());
826 }
827 }
828 else if ( options.mode == modeObjCProtocols ) {
829 if ( dyldCache->objcOpt() == nullptr ) {
830 fprintf(stderr, "Error: could not get optimized objc\n");
831 return 1;
832 }
833 objc_opt::objc_protocolopt2_t* protocols = dyldCache->objcOpt()->protocolopt2();
834 if ( protocols == nullptr ) {
835 fprintf(stderr, "Error: could not get optimized objc protocols\n");
836 return 1;
837 }
838
839 for (uint64_t index = 0; index != protocols->capacity; ++index) {
840 const objc_opt::objc_classheader_t& clshi = protocols->classOffsets()[index];
841 if ( clshi.clsOffset == 0 ) {
842 fprintf(stderr, "[% 5lld]\n", index);
843 continue;
844 }
845 const char* name = (const char*)(((const uint8_t*)protocols) + protocols->offsets()[index]);
846 if ( !clshi.isDuplicate() ) {
847 fprintf(stderr, "[% 5lld] -> (% 8d, % 8d) = %s\n", index, clshi.clsOffset, clshi.hiOffset, name);
848 continue;
849 }
850
851 // class appears in more than one header
852 uint32_t count = clshi.duplicateCount();
853 fprintf(stderr, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n",
854 index, clshi.duplicateIndex(), clshi.duplicateIndex() + clshi.duplicateCount() - 1, name);
855
856 const objc_opt::objc_classheader_t *list = &protocols->duplicateOffsets()[clshi.duplicateIndex()];
857 for (uint32_t i = 0; i < count; i++) {
858 fprintf(stderr, " - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi.duplicateIndex() + i), list[i].clsOffset, list[i].hiOffset);
859 }
860 }
861 }
862 else if ( options.mode == modeExtract ) {
863 char pathBuffer[PATH_MAX];
864 uint32_t bufferSize = PATH_MAX;
865 if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) {
866 fprintf(stderr, "Error: could not get path of program\n");
867 return 1;
868 }
869 char* last = strrchr(pathBuffer, '/');
870 // The bundle is at a different location on device. Its /usr/lib/dsc_extractor.bundle in the SDK
871 // but /usr/local/lib/dsc_extractor.bundle on device.
872 strcpy(last+1, DSC_BUNDLE_REL_PATH);
873 void* handle = dlopen(pathBuffer, RTLD_LAZY);
874 if ( handle == NULL ) {
875 fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer);
876 return 1;
877 }
878
879 typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
880 void (^progress)(unsigned current, unsigned total));
881
882 extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
883 if ( proc == NULL ) {
884 fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
885 return 1;
886 }
887
888 int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } );
889 return result;
890 }
891 else {
892 switch ( options.mode ) {
893 case modeList: {
894 if (options.printInodes) {
895 dyldCache->forEachImageEntry(^(const char* path, uint64_t mTime, uint64_t inode) {
896 printf("0x%08llX 0x%08llX ", inode, mTime);
897 if ( isAlias(path, dyldCache) )
898 printf("[alias] %s\n", path);
899 else
900 printf("%s\n", path);
901 });
902 } else {
903 dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char *dylibUUID, const char *installName, bool &stop) {
904 if ( options.printVMAddrs )
905 printf("0x%08llX ", loadAddressUnslid);
906 if ( options.printUUIDs ) {
907 const uint8_t* uuid = (uint8_t*)dylibUUID;
908 printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ",
909 uuid[0], uuid[1], uuid[2], uuid[3],
910 uuid[4], uuid[5], uuid[6], uuid[7],
911 uuid[8], uuid[9], uuid[10], uuid[11],
912 uuid[12], uuid[13], uuid[14], uuid[15]);
913 }
914 if ( isAlias(installName, dyldCache) )
915 printf("[alias] %s\n", installName);
916 else
917 printf("%s\n", installName);
918 });
919 }
920 break;
921 }
922 case modeMap: {
923 __block std::map<uint64_t, const char*> dataSegNames;
924 __block std::map<uint64_t, uint64_t> dataSegEnds;
925 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
926 dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
927 mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
928 if ( isAlias(installName, dyldCache) )
929 return;
930 printf("0x%08llX - 0x%08llX %s %s\n", info.vmAddr, info.vmAddr + info.vmSize, info.segName, installName);
931 if ( strncmp(info.segName, "__DATA", 6) == 0 ) {
932 dataSegNames[info.vmAddr] = installName;
933 dataSegEnds[info.vmAddr] = info.vmAddr + info.vmSize;
934 }
935 });
936 });
937 // <rdar://problem/51084507> Enhance dyld_shared_cache_util to show where section alignment added padding
938 uint64_t lastEnd = 0;
939 for (const auto& entry : dataSegEnds) {
940 uint64_t padding = entry.first - lastEnd;
941 if ( (padding > 32) && (lastEnd != 0) ) {
942 printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd, entry.first, padding/1024);
943 }
944 lastEnd = entry.second;
945 }
946 break;
947 }
948 case modeDependencies: {
949 __block bool dependentTargetFound = false;
950 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
951 if ( strcmp(options.dependentsOfPath, installName) != 0 )
952 return;
953 dependentTargetFound = true;
954
955 auto printDep = [&options](const char *loadPath, uint32_t compatVersion, uint32_t curVersion) {
956 if ( options.printDylibVersions ) {
957 uint32_t compat_vers = compatVersion;
958 uint32_t current_vers = curVersion;
959 printf("\t%s", loadPath);
960 if ( compat_vers != 0xFFFFFFFF ) {
961 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
962 (compat_vers >> 16),
963 (compat_vers >> 8) & 0xff,
964 (compat_vers) & 0xff,
965 (current_vers >> 16),
966 (current_vers >> 8) & 0xff,
967 (current_vers) & 0xff);
968 }
969 else {
970 printf("\n");
971 }
972 }
973 else {
974 printf("\t%s\n", loadPath);
975 }
976 };
977
978 dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
979
980 // First print out our dylib and version.
981 const char* dylibInstallName;
982 uint32_t currentVersion;
983 uint32_t compatVersion;
984 if ( mf->getDylibInstallName(&dylibInstallName, &compatVersion, &currentVersion) ) {
985 printDep(dylibInstallName, compatVersion, currentVersion);
986 }
987
988 // Then the dependent dylibs.
989 mf->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
990 printDep(loadPath, compatVersion, curVersion);
991 });
992 });
993 if (options.dependentsOfPath && !dependentTargetFound) {
994 fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath);
995 exit(1);
996 }
997 break;
998 }
999 case modeLinkEdit: {
1000 std::map<uint32_t, const char*> pageToContent;
1001 auto add_linkedit = [&pageToContent](uint32_t pageStart, uint32_t pageEnd, const char* message) {
1002 for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
1003 std::map<uint32_t, const char*>::iterator pos = pageToContent.find(p);
1004 if ( pos == pageToContent.end() ) {
1005 pageToContent[p] = strdup(message);
1006 }
1007 else {
1008 const char* oldMessage = pos->second;
1009 char* newMesssage;
1010 asprintf(&newMesssage, "%s, %s", oldMessage, message);
1011 pageToContent[p] = newMesssage;
1012 ::free((void*)oldMessage);
1013 }
1014 }
1015 };
1016
1017 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1018 // Filter out symlinks.
1019 if (isAlias(installName, dyldCache))
1020 return;
1021 dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1022 Diagnostics diag;
1023 dyld3::MachOAnalyzer::LinkEditInfo leInfo;
1024 ma->getLinkEditPointers(diag, leInfo);
1025
1026 if (diag.hasError())
1027 return;
1028
1029 char message[1000];
1030 const char* shortName = strrchr(installName, '/') + 1;
1031 // add export trie info
1032 if ( leInfo.dyldInfo->export_size != 0 ) {
1033 //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off());
1034 uint32_t exportPageOffsetStart = leInfo.dyldInfo->export_off & (-4096);
1035 uint32_t exportPageOffsetEnd = (leInfo.dyldInfo->export_off + leInfo.dyldInfo->export_size) & (-4096);
1036 sprintf(message, "exports from %s", shortName);
1037 add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message);
1038 }
1039 // add binding info
1040 if ( leInfo.dyldInfo->bind_size != 0 ) {
1041 uint32_t bindPageOffsetStart = leInfo.dyldInfo->bind_off & (-4096);
1042 uint32_t bindPageOffsetEnd = (leInfo.dyldInfo->bind_off + leInfo.dyldInfo->bind_size) & (-4096);
1043 sprintf(message, "bindings from %s", shortName);
1044 add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message);
1045 }
1046 // add lazy binding info
1047 if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
1048 uint32_t lazybindPageOffsetStart = leInfo.dyldInfo->lazy_bind_off & (-4096);
1049 uint32_t lazybindPageOffsetEnd = (leInfo.dyldInfo->lazy_bind_off + leInfo.dyldInfo->lazy_bind_size) & (-4096);
1050 sprintf(message, "lazy bindings from %s", shortName);
1051 add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message);
1052 }
1053 // add weak binding info
1054 if ( leInfo.dyldInfo->weak_bind_size != 0 ) {
1055 uint32_t weakbindPageOffsetStart = leInfo.dyldInfo->weak_bind_off & (-4096);
1056 uint32_t weakbindPageOffsetEnd = (leInfo.dyldInfo->weak_bind_off + leInfo.dyldInfo->weak_bind_size) & (-4096);
1057 sprintf(message, "weak bindings from %s", shortName);
1058 add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message);
1059 }
1060 });
1061
1062 for (std::map<uint32_t, const char*>::iterator it = pageToContent.begin(); it != pageToContent.end(); ++it) {
1063 printf("0x%08X %s\n", it->first, it->second);
1064 }
1065 break;
1066 }
1067 case modeSize: {
1068 struct TextInfo {
1069 uint64_t textSize;
1070 const char* path;
1071 };
1072 __block std::vector<TextInfo> textSegments;
1073 dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
1074 // Filter out symlinks.
1075 if (isAlias(installName, dyldCache))
1076 return;
1077
1078 dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1079 ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
1080 if ( strcmp(info.segName, "__TEXT") != 0 )
1081 return;
1082 textSegments.push_back({ info.fileSize, installName });
1083 });
1084 });
1085 std::sort(textSegments.begin(), textSegments.end(), [](const TextInfo& left, const TextInfo& right) {
1086 return (left.textSize > right.textSize);
1087 });
1088 for (std::vector<TextInfo>::iterator it = textSegments.begin(); it != textSegments.end(); ++it) {
1089 printf(" 0x%08llX %s\n", it->textSize, it->path);
1090 }
1091 break;
1092 }
1093 case modeNone:
1094 case modeInfo:
1095 case modeSlideInfo:
1096 case modeVerboseSlideInfo:
1097 case modeAcceleratorInfo:
1098 case modeTextInfo:
1099 case modeLocalSymbols:
1100 case modeJSONMap:
1101 case modeJSONDependents:
1102 case modeSectionSizes:
1103 case modeStrings:
1104 case modeObjCProtocols:
1105 case modeExtract:
1106 break;
1107 }
1108 }
1109 return 0;
1110 }
1111