dyld-625.13.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.h>
38 #include <mach-o/dyld_priv.h>
39 #include <mach/mach.h>
40
41 #include <map>
42 #include <vector>
43
44 #include "DyldSharedCache.h"
45
46 #include "dsc_iterator.h"
47 #include "dsc_extractor.h"
48 #include "dyld_cache_format.h"
49 #include "Architectures.hpp"
50 #include "MachOFileAbstraction.hpp"
51 #include "CacheFileAbstraction.hpp"
52 #include "Trie.hpp"
53 #include "SupportedArchs.h"
54
55 enum Mode {
56 modeNone,
57 modeList,
58 modeMap,
59 modeDependencies,
60 modeSlideInfo,
61 modeVerboseSlideInfo,
62 modeAcceleratorInfo,
63 modeTextInfo,
64 modeLinkEdit,
65 modeLocalSymbols,
66 modeStrings,
67 modeInfo,
68 modeSize,
69 modeExtract
70 };
71
72 struct Options {
73 Mode mode;
74 const char* dependentsOfPath;
75 const void* mappedCache;
76 const char* extractionDir;
77 bool printUUIDs;
78 bool printVMAddrs;
79 bool printDylibVersions;
80 bool printInodes;
81 };
82
83 struct TextInfo {
84 uint64_t textSize;
85 const char* path;
86 };
87
88 struct TextInfoSorter {
89 bool operator()(const TextInfo& left, const TextInfo& right) {
90 return (left.textSize > right.textSize);
91 }
92 };
93
94 struct Results {
95 std::map<uint32_t, const char*> pageToContent;
96 uint64_t linkeditBase;
97 bool dependentTargetFound;
98 std::vector<TextInfo> textSegments;
99 };
100
101
102 // mmap() an shared cache file read/only but laid out like it would be at runtime
103 static const DyldSharedCache* mapCacheFile(const char* path, size_t& cacheLength)
104 {
105 struct stat statbuf;
106 if ( ::stat(path, &statbuf) ) {
107 fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path);
108 return nullptr;
109 }
110
111 int cache_fd = ::open(path, O_RDONLY);
112 if (cache_fd < 0) {
113 fprintf(stderr, "Error: failed to open shared cache file at %s\n", path);
114 return nullptr;
115 }
116
117 uint8_t firstPage[4096];
118 if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
119 fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
120 return nullptr;
121 }
122 const dyld_cache_header* header = (dyld_cache_header*)firstPage;
123 const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
124
125 size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
126 vm_address_t result;
127 kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
128 if ( r != KERN_SUCCESS ) {
129 fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
130 return nullptr;
131 }
132 for (int i=0; i < 3; ++i) {
133 void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
134 PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
135 if (mapped_cache == MAP_FAILED) {
136 fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", path, errno);
137 return nullptr;
138 }
139 }
140 ::close(cache_fd);
141
142 cacheLength = statbuf.st_size;
143
144 return (DyldSharedCache*)result;
145 }
146
147
148
149 void usage() {
150 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");
151 }
152
153 #if __x86_64__
154 static bool isHaswell()
155 {
156 // check system is capable of running x86_64h code
157 struct host_basic_info info;
158 mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
159 mach_port_t hostPort = mach_host_self();
160 kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
161 mach_port_deallocate(mach_task_self(), hostPort);
162 if ( result != KERN_SUCCESS )
163 return false;
164 return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H );
165 }
166 #endif
167
168 /*
169 * Get the path to the native shared cache for this host
170 */
171 static const char* default_shared_cache_path() {
172 #if __i386__
173 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386";
174 #elif __x86_64__
175 if ( isHaswell() )
176 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64h";
177 else
178 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64";
179 #elif __ARM_ARCH_5TEJ__
180 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5";
181 #elif __ARM_ARCH_6K__
182 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6";
183 #elif __ARM_ARCH_7K__
184 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7k";
185 #elif __ARM_ARCH_7A__
186 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7";
187 #elif __ARM_ARCH_7F__
188 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7f";
189 #elif __ARM_ARCH_7S__
190 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7s";
191 #elif __ARM64_ARCH_8_32__
192 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64_32";
193 #elif __arm64e__
194 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e";
195 #elif __arm64__
196 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64";
197 #else
198 #error unsupported architecture
199 #endif
200 }
201
202 typedef void (*segment_callback_t)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
203 const Options& options, Results& results);
204
205
206
207 /*
208 * List dependencies from the mach-o header at headerAddr
209 * in the same format as 'otool -L'
210 */
211 template <typename A>
212 void print_dependencies(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
213 const Options& options, Results& results) {
214 typedef typename A::P P;
215 typedef typename A::P::E E;
216
217 if ( strcmp(options.dependentsOfPath, dylibInfo->path) != 0 )
218 return;
219 if ( strcmp(segInfo->name, "__TEXT") != 0 )
220 return;
221
222 const macho_dylib_command<P>* dylib_cmd;
223 const macho_header<P>* mh = (const macho_header<P>*)dylibInfo->machHeader;
224 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uintptr_t)dylibInfo->machHeader + sizeof(macho_header<P>));
225 const uint32_t cmd_count = mh->ncmds();
226 const macho_load_command<P>* cmd = cmds;
227 for (uint32_t i = 0; i < cmd_count; ++i) {
228 switch ( cmd->cmd() ) {
229 case LC_LOAD_DYLIB:
230 case LC_ID_DYLIB:
231 case LC_REEXPORT_DYLIB:
232 case LC_LOAD_WEAK_DYLIB:
233 case LC_LOAD_UPWARD_DYLIB:
234 dylib_cmd = (macho_dylib_command<P>*)cmd;
235 if ( options.printDylibVersions ) {
236 uint32_t compat_vers = dylib_cmd->compatibility_version();
237 uint32_t current_vers = dylib_cmd->current_version();
238 printf("\t%s", dylib_cmd->name());
239 if ( compat_vers != 0xFFFFFFFF ) {
240 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
241 (compat_vers >> 16),
242 (compat_vers >> 8) & 0xff,
243 (compat_vers) & 0xff,
244 (current_vers >> 16),
245 (current_vers >> 8) & 0xff,
246 (current_vers) & 0xff);
247 }
248 else {
249 printf("\n");
250 }
251 }
252 else {
253 printf("\t%s\n", dylib_cmd->name());
254 }
255 break;
256 }
257 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
258 }
259 results.dependentTargetFound = true;
260 }
261
262 /*
263 * Print out a dylib from the shared cache, optionally including the UUID or unslid load address
264 */
265 template <typename A>
266 void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
267 const Options& options, Results& results)
268 {
269 if ( strcmp(segInfo->name, "__TEXT") != 0 )
270 return;
271
272 if ( options.printVMAddrs )
273 printf("0x%08llX ", segInfo->address);
274 if ( options.printInodes )
275 printf("0x%08llX 0x%08llX ", dylibInfo->inode, dylibInfo->modTime);
276 if ( options.printUUIDs ) {
277 if ( dylibInfo->uuid != NULL ) {
278 const uint8_t* uuid = (uint8_t*)dylibInfo->uuid;;
279 printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ",
280 uuid[0], uuid[1], uuid[2], uuid[3],
281 uuid[4], uuid[5], uuid[6], uuid[7],
282 uuid[8], uuid[9], uuid[10], uuid[11],
283 uuid[12], uuid[13], uuid[14], uuid[15]);
284 }
285 else
286 printf("< no uuid in dylib > ");
287 }
288 if ( dylibInfo->isAlias )
289 printf("[alias] %s\n", dylibInfo->path);
290 else
291 printf("%s\n", dylibInfo->path);
292 }
293
294
295 template <typename A>
296 void collect_size(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
297 const Options& options, Results& results)
298 {
299 if ( strcmp(segInfo->name, "__TEXT") != 0 )
300 return;
301 if ( dylibInfo->isAlias )
302 return;
303
304 TextInfo info;
305 info.textSize = segInfo->fileSize;
306 info.path = dylibInfo->path;
307 results.textSegments.push_back(info);
308 }
309
310
311
312
313 static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, const char* message, Results& results)
314 {
315 for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
316 std::map<uint32_t, const char*>::iterator pos = results.pageToContent.find(p);
317 if ( pos == results.pageToContent.end() ) {
318 results.pageToContent[p] = strdup(message);
319 }
320 else {
321 const char* oldMessage = pos->second;
322 char* newMesssage;
323 asprintf(&newMesssage, "%s, %s", oldMessage, message);
324 results.pageToContent[p] = newMesssage;
325 ::free((void*)oldMessage);
326 }
327 }
328 }
329
330
331 /*
332 * get LINKEDIT info for dylib
333 */
334 template <typename A>
335 void process_linkedit(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
336 const Options& options, Results& results) {
337 typedef typename A::P P;
338 typedef typename A::P::E E;
339 // filter out symlinks
340 if ( dylibInfo->isAlias )
341 return;
342 const macho_header<P>* mh = (const macho_header<P>*)dylibInfo->machHeader;
343 uint32_t ncmds = mh->ncmds();
344 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((long)mh + sizeof(macho_header<P>));
345 const macho_load_command<P>* cmd = cmds;
346 for (uint32_t i = 0; i < ncmds; i++) {
347 if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) {
348 macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
349 char message[1000];
350 const char* shortName = strrchr(dylibInfo->path, '/') + 1;
351 // add export trie info
352 if ( dyldInfo->export_size() != 0 ) {
353 //printf("export_off=0x%X\n", dyldInfo->export_off());
354 uint32_t exportPageOffsetStart = dyldInfo->export_off() & (-4096);
355 uint32_t exportPageOffsetEnd = (dyldInfo->export_off() + dyldInfo->export_size()) & (-4096);
356 sprintf(message, "exports from %s", shortName);
357 add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message, results);
358 }
359 // add binding info
360 if ( dyldInfo->bind_size() != 0 ) {
361 uint32_t bindPageOffsetStart = dyldInfo->bind_off() & (-4096);
362 uint32_t bindPageOffsetEnd = (dyldInfo->bind_off() + dyldInfo->bind_size()) & (-4096);
363 sprintf(message, "bindings from %s", shortName);
364 add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message, results);
365 }
366 // add lazy binding info
367 if ( dyldInfo->lazy_bind_size() != 0 ) {
368 uint32_t lazybindPageOffsetStart = dyldInfo->lazy_bind_off() & (-4096);
369 uint32_t lazybindPageOffsetEnd = (dyldInfo->lazy_bind_off() + dyldInfo->lazy_bind_size()) & (-4096);
370 sprintf(message, "lazy bindings from %s", shortName);
371 add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message, results);
372 }
373 // add weak binding info
374 if ( dyldInfo->weak_bind_size() != 0 ) {
375 uint32_t weakbindPageOffsetStart = dyldInfo->weak_bind_off() & (-4096);
376 uint32_t weakbindPageOffsetEnd = (dyldInfo->weak_bind_off() + dyldInfo->weak_bind_size()) & (-4096);
377 sprintf(message, "weak bindings from %s", shortName);
378 add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message, results);
379 }
380 }
381 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
382 }
383 }
384
385
386 /*
387 * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built
388 */
389 template <typename A>
390 void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, const Options& options, Results& results) {
391 if ( !dylibInfo->isAlias )
392 printf("0x%08llX - 0x%08llX %s %s\n", segInfo->address, segInfo->address + segInfo->fileSize, segInfo->name, dylibInfo->path);
393 }
394
395
396 static void checkMode(Mode mode) {
397 if ( mode != modeNone ) {
398 fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n");
399 usage();
400 exit(1);
401 }
402 }
403
404 int main (int argc, const char* argv[]) {
405
406 const char* sharedCachePath = default_shared_cache_path();
407
408 Options options;
409 options.mode = modeNone;
410 options.printUUIDs = false;
411 options.printVMAddrs = false;
412 options.printDylibVersions = false;
413 options.printInodes = false;
414 options.dependentsOfPath = NULL;
415 options.extractionDir = NULL;
416
417 bool printStrings = false;
418 bool printExports = false;
419
420 for (uint32_t i = 1; i < argc; i++) {
421 const char* opt = argv[i];
422 if (opt[0] == '-') {
423 if (strcmp(opt, "-list") == 0) {
424 checkMode(options.mode);
425 options.mode = modeList;
426 }
427 else if (strcmp(opt, "-dependents") == 0) {
428 checkMode(options.mode);
429 options.mode = modeDependencies;
430 options.dependentsOfPath = argv[++i];
431 if ( i >= argc ) {
432 fprintf(stderr, "Error: option -depdendents requires an argument\n");
433 usage();
434 exit(1);
435 }
436 }
437 else if (strcmp(opt, "-linkedit") == 0) {
438 checkMode(options.mode);
439 options.mode = modeLinkEdit;
440 }
441 else if (strcmp(opt, "-info") == 0) {
442 checkMode(options.mode);
443 options.mode = modeInfo;
444 }
445 else if (strcmp(opt, "-slide_info") == 0) {
446 checkMode(options.mode);
447 options.mode = modeSlideInfo;
448 }
449 else if (strcmp(opt, "-verbose_slide_info") == 0) {
450 checkMode(options.mode);
451 options.mode = modeVerboseSlideInfo;
452 }
453 else if (strcmp(opt, "-accelerator_info") == 0) {
454 checkMode(options.mode);
455 options.mode = modeAcceleratorInfo;
456 }
457 else if (strcmp(opt, "-text_info") == 0) {
458 checkMode(options.mode);
459 options.mode = modeTextInfo;
460 }
461 else if (strcmp(opt, "-local_symbols") == 0) {
462 checkMode(options.mode);
463 options.mode = modeLocalSymbols;
464 }
465 else if (strcmp(opt, "-strings") == 0) {
466 if (options.mode != modeStrings)
467 checkMode(options.mode);
468 options.mode = modeStrings;
469 printStrings = true;
470 }
471 else if (strcmp(opt, "-exports") == 0) {
472 if (options.mode != modeStrings)
473 checkMode(options.mode);
474 options.mode = modeStrings;
475 printExports = true;
476 }
477 else if (strcmp(opt, "-map") == 0) {
478 checkMode(options.mode);
479 options.mode = modeMap;
480 }
481 else if (strcmp(opt, "-size") == 0) {
482 checkMode(options.mode);
483 options.mode = modeSize;
484 }
485 else if (strcmp(opt, "-extract") == 0) {
486 checkMode(options.mode);
487 options.mode = modeExtract;
488 options.extractionDir = argv[++i];
489 if ( i >= argc ) {
490 fprintf(stderr, "Error: option -extract requires a directory argument\n");
491 usage();
492 exit(1);
493 }
494 }
495 else if (strcmp(opt, "-uuid") == 0) {
496 options.printUUIDs = true;
497 }
498 else if (strcmp(opt, "-inode") == 0) {
499 options.printInodes = true;
500 }
501 else if (strcmp(opt, "-versions") == 0) {
502 options.printDylibVersions = true;
503 }
504 else if (strcmp(opt, "-vmaddr") == 0) {
505 options.printVMAddrs = true;
506 }
507 else {
508 fprintf(stderr, "Error: unrecognized option %s\n", opt);
509 usage();
510 exit(1);
511 }
512 }
513 else {
514 sharedCachePath = opt;
515 }
516 }
517
518 if ( options.mode == modeNone ) {
519 fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
520 usage();
521 exit(1);
522 }
523
524 if ( options.mode != modeSlideInfo && options.mode != modeVerboseSlideInfo ) {
525 if ( options.printUUIDs && (options.mode != modeList) )
526 fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
527
528 if ( options.printVMAddrs && (options.mode != modeList) )
529 fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
530
531 if ( options.printDylibVersions && (options.mode != modeDependencies) )
532 fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
533
534 if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) {
535 fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
536 usage();
537 exit(1);
538 }
539 }
540
541 const DyldSharedCache* dyldCache = nullptr;
542 bool dyldCacheIsLive = true;
543 size_t cacheLength = 0;
544 if ( sharedCachePath != nullptr ) {
545 dyldCache = mapCacheFile(sharedCachePath, cacheLength);
546 dyldCacheIsLive = false;
547 }
548 else {
549 #if __MAC_OS_X_VERSION_MIN_REQUIRED && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
550 fprintf(stderr, "this tool needs to run on macOS 10.13 or later\n");
551 return 1;
552 #else
553 dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
554 #endif
555 }
556
557 options.mappedCache = dyldCache;
558
559 if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) {
560 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
561 if ( header->slideInfoOffset() == 0 ) {
562 fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
563 exit(1);
564 }
565 const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
566 const dyldCacheFileMapping<LittleEndian>* textMapping = &mappings[0];
567 const dyldCacheFileMapping<LittleEndian>* dataMapping = &mappings[1];
568 const dyldCacheFileMapping<LittleEndian>* linkEditMapping = &mappings[2];
569 uint64_t dataStartAddress = dataMapping->address();
570 uint64_t dataSize = dataMapping->size();
571 uint64_t slideInfoMappedOffset = (header->slideInfoOffset()-linkEditMapping->file_offset()) + (linkEditMapping->address() - textMapping->address());
572 const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)options.mappedCache+slideInfoMappedOffset);
573 printf("slide info version=%d\n", slideInfoHeader->version());
574 if ( slideInfoHeader->version() == 1 ) {
575 printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096);
576 const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset());
577 for(int i=0; i < slideInfoHeader->toc_count(); ++i) {
578 printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i));
579 const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)];
580 for(int j=0; j < slideInfoHeader->entries_size(); ++j)
581 printf("%02X", entry->bits[j]);
582 printf("\n");
583 }
584 }
585 else if ( slideInfoHeader->version() == 2 ) {
586 const dyldCacheSlideInfo2<LittleEndian>* slideInfo = (dyldCacheSlideInfo2<LittleEndian>*)(slideInfoHeader);
587 printf("page_size=%d\n", slideInfo->page_size());
588 printf("delta_mask=0x%016llX\n", slideInfo->delta_mask());
589 printf("value_add=0x%016llX\n", slideInfo->value_add());
590 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count(), slideInfo->page_extras_count());
591 const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset());
592 const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset());
593 for (int i=0; i < slideInfo->page_starts_count(); ++i) {
594 const uint16_t start = starts[i];
595 auto rebaseChain = [&](uint8_t* pageContent, uint16_t startOffset)
596 {
597 uintptr_t slideAmount = 0;
598 const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask());
599 const uintptr_t valueMask = ~deltaMask;
600 const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add());
601 const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
602
603 uint32_t pageOffset = startOffset;
604 uint32_t delta = 1;
605 while ( delta != 0 ) {
606 uint8_t* loc = pageContent + pageOffset;
607 uintptr_t rawValue = *((uintptr_t*)loc);
608 delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
609 uintptr_t value = (rawValue & valueMask);
610 if ( value != 0 ) {
611 value += valueAdd;
612 value += slideAmount;
613 }
614 printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue);
615 pageOffset += delta;
616 }
617 };
618 const uint8_t* dataPagesStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
619 if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
620 printf("page[% 5d]: no rebasing\n", i);
621 }
622 else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
623 printf("page[% 5d]: ", i);
624 int j=(start & 0x3FFF);
625 bool done = false;
626 do {
627 uint16_t aStart = extras[j];
628 printf("start=0x%04X ", aStart & 0x3FFF);
629 if ( options.mode == modeVerboseSlideInfo ) {
630 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size()*i));
631 uint16_t pageStartOffset = (aStart & 0x3FFF)*4;
632 rebaseChain(page, pageStartOffset);
633 }
634 done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
635 ++j;
636 } while ( !done );
637 printf("\n");
638 }
639 else {
640 printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
641 if ( options.mode == modeVerboseSlideInfo ) {
642 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size()*i));
643 uint16_t pageStartOffset = start*4;
644 rebaseChain(page, pageStartOffset);
645 }
646 }
647 }
648 }
649 else if ( slideInfoHeader->version() == 3 ) {
650 const dyldCacheSlideInfo3<LittleEndian>* slideInfo = (dyldCacheSlideInfo3<LittleEndian>*)(slideInfoHeader);
651 printf("page_size=%d\n", slideInfo->page_size());
652 printf("page_starts_count=%d\n", slideInfo->page_starts_count());
653 printf("auth_value_add=0x%016llX\n", slideInfo->auth_value_add());
654 const uint8_t* dataSegmentStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
655 for (int i=0; i < slideInfo->page_starts_count(); ++i) {
656 const uint16_t start = slideInfo->page_starts(i);
657 if ( start == 0xFFFF ) {
658 printf("page[% 5d]: no rebasing\n", i);
659 }
660 else {
661 printf("page[% 5d]: start=0x%04X\n", i, start);
662 if ( options.mode == modeVerboseSlideInfo ) {
663 typedef Pointer64<LittleEndian> P;
664 typedef typename P::uint_t pint_t;
665 const uint8_t* pageStart = dataSegmentStart + (i * slideInfo->page_size());
666 pint_t delta = start;
667 const uint8_t* rebaseLocation = pageStart;
668 do {
669 rebaseLocation += delta;
670 pint_t value = (pint_t)P::getP(*(uint64_t*)rebaseLocation);
671 delta = ( (value & 0x3FF8000000000000) >> 51) * sizeof(pint_t);
672
673 // Regular pointer which needs to fit in 51-bits of value.
674 // C++ RTTI uses the top bit, so we'll allow the whole top-byte
675 // and the signed-extended bottom 43-bits to be fit in to 51-bits.
676 uint64_t top8Bits = value & 0x007F80000000000ULL;
677 uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
678 uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
679 printf(" [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)(rebaseLocation - pageStart), targetValue);
680 } while (delta != 0);
681 }
682 }
683 }
684 }
685 else if ( slideInfoHeader->version() == 4 ) {
686 const dyld_cache_slide_info4* slideInfo = (dyld_cache_slide_info4*)(slideInfoHeader);
687 printf("page_size=%d\n", slideInfo->page_size);
688 printf("delta_mask=0x%016llX\n", slideInfo->delta_mask);
689 printf("value_add=0x%016llX\n", slideInfo->value_add);
690 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count, slideInfo->page_extras_count);
691 const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset);
692 const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset);
693 for (int i=0; i < slideInfo->page_starts_count; ++i) {
694 const uint16_t start = starts[i];
695 auto rebaseChainV4 = [&](uint8_t* pageContent, uint16_t startOffset)
696 {
697 uintptr_t slideAmount = 0;
698 const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask);
699 const uintptr_t valueMask = ~deltaMask;
700 const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add);
701 const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2;
702
703 uint32_t pageOffset = startOffset;
704 uint32_t delta = 1;
705 while ( delta != 0 ) {
706 uint8_t* loc = pageContent + pageOffset;
707 uint32_t rawValue = *((uint32_t*)loc);
708 delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
709 uintptr_t value = (rawValue & valueMask);
710 if ( (value & 0xFFFF8000) == 0 ) {
711 // small positive non-pointer, use as-is
712 }
713 else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
714 // small negative non-pointer
715 value |= 0xC0000000;
716 }
717 else {
718 value += valueAdd;
719 value += slideAmount;
720 }
721 printf(" [% 5d + 0x%04X]: 0x%08X\n", i, pageOffset, rawValue);
722 pageOffset += delta;
723 }
724 };
725 const uint8_t* dataPagesStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
726 if ( start == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
727 printf("page[% 5d]: no rebasing\n", i);
728 }
729 else if ( start & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
730 printf("page[% 5d]: ", i);
731 int j=(start & DYLD_CACHE_SLIDE4_PAGE_INDEX);
732 bool done = false;
733 do {
734 uint16_t aStart = extras[j];
735 printf("start=0x%04X ", aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX);
736 if ( options.mode == modeVerboseSlideInfo ) {
737 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
738 uint16_t pageStartOffset = (aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX)*4;
739 rebaseChainV4(page, pageStartOffset);
740 }
741 done = (extras[j] & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
742 ++j;
743 } while ( !done );
744 printf("\n");
745 }
746 else {
747 printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
748 if ( options.mode == modeVerboseSlideInfo ) {
749 uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
750 uint16_t pageStartOffset = start*4;
751 rebaseChainV4(page, pageStartOffset);
752 }
753 }
754 }
755 }
756 return 0;
757 }
758
759 if ( options.mode == modeInfo ) {
760 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
761 printf("uuid: ");
762 if ( header->mappingOffset() >= 0x68 ) {
763 const uint8_t* uuid = header->uuid();
764 printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
765 uuid[0], uuid[1], uuid[2], uuid[3],
766 uuid[4], uuid[5], uuid[6], uuid[7],
767 uuid[8], uuid[9], uuid[10], uuid[11],
768 uuid[12], uuid[13], uuid[14], uuid[15]);
769 }
770 else {
771 printf("n/a\n");
772 }
773 if ( header->mappingOffset() >= 0xE0 ) {
774 // HACK until this uses new header
775 uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8));
776 uint32_t bitfield = *((uint32_t*)(((char*)header) + 0xDC));
777 uint32_t simulator = bitfield & 0x200;
778 uint32_t locallyBuiltCache = bitfield & 0x400;
779 switch (platform) {
780 case 1:
781 printf("platform: macOS\n");
782 break;
783 case 2:
784 if ( simulator )
785 printf("platform: iOS simulator\n");
786 else
787 printf("platform: iOS\n");
788 break;
789 case 3:
790 if ( simulator )
791 printf("platform: tvOS simulator\n");
792 else
793 printf("platform: tvOS\n");
794 break;
795 case 4:
796 if ( simulator )
797 printf("platform: watchOS simulator\n");
798 else
799 printf("platform: watchOS\n");
800 break;
801 case 5:
802 printf("platform: bridgeOS\n");
803 break;
804 default:
805 printf("platform: 0x%08X 0x%08X\n", platform, simulator);
806 }
807 printf("built by: %s\n", locallyBuiltCache ? "local machine" : "B&I");
808 }
809 printf("image count: %u\n", header->imagesCount());
810 if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) {
811 printf("branch pool count: %u\n", header->branchPoolsCount());
812 }
813 printf("mappings:\n");
814 const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
815 for (uint32_t i=0; i < header->mappingCount(); ++i) {
816 if ( mappings[i].init_prot() & VM_PROT_EXECUTE )
817 printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
818 mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
819 mappings[i].address(), mappings[i].address() + mappings[i].size());
820 else if ( mappings[i]. init_prot() & VM_PROT_WRITE )
821 printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
822 mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
823 mappings[i].address(), mappings[i].address() + mappings[i].size());
824 else if ( mappings[i].init_prot() & VM_PROT_READ )
825 printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
826 mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
827 mappings[i].address(), mappings[i].address() + mappings[i].size());
828 }
829 if ( header->codeSignatureOffset() != 0 ) {
830 uint64_t size = cacheLength - header->codeSignatureOffset();
831 uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size();
832 if ( size != 0 )
833 printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
834 size/(1024*1024), header->codeSignatureOffset(), header->codeSignatureOffset() + size, csAddr, csAddr + size);
835 }
836 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
837 header->slideInfoSize()/1024, header->slideInfoOffset(), header->slideInfoOffset() + header->slideInfoSize());
838 if ( header->localSymbolsOffset() != 0 )
839 printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
840 header->localSymbolsSize()/(1024*1024), header->localSymbolsOffset(), header->localSymbolsOffset() + header->localSymbolsSize());
841 if ( (header->mappingOffset() >= 0x78) && (header->accelerateInfoSize() != 0) )
842 printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n",
843 header->accelerateInfoSize()/1024, header->accelerateInfoAddr(), header->accelerateInfoAddr() + header->accelerateInfoSize());
844 }
845 else if ( options.mode == modeAcceleratorInfo ) {
846 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
847 if ( (header->mappingOffset() < sizeof(dyldCacheHeader<LittleEndian>)) || (header->accelerateInfoSize() == 0) ) {
848 printf("no accelerator info\n");
849 }
850 else {
851 const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
852 uint64_t aiAddr = header->accelerateInfoAddr();
853 dyldCacheAcceleratorInfo<LittleEndian>* accelInfo = NULL;
854 for (uint32_t i=0; i < header->mappingCount(); ++i) {
855 if ( (mappings[i].address() <= aiAddr) && (aiAddr < mappings[i].address()+mappings[i].size()) ) {
856 uint64_t offset = aiAddr - mappings[i].address() + mappings[i].file_offset();
857 accelInfo = (dyldCacheAcceleratorInfo<LittleEndian>*)((uint8_t*)options.mappedCache + offset);
858 }
859 }
860 if ( accelInfo == NULL ) {
861 printf("accelerator info not in any mapped range\n");
862 }
863 else {
864 const dyldCacheImageInfo<LittleEndian>* images = (dyldCacheImageInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesOffset());
865 const dyldCacheImageInfoExtra<LittleEndian>* imagesExtra = (dyldCacheImageInfoExtra<LittleEndian>*)((char*)accelInfo + accelInfo->imagesExtrasOffset());
866 const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset());
867 const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset());
868 printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount());
869 for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) {
870 printf(" image[%3u] %s:\n", i, (char*)options.mappedCache +images[i].pathFileOffset());
871 printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr(), imagesExtra[i].exportsTrieSize());
872 if ( imagesExtra[i].weakBindingsSize() )
873 printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr(), imagesExtra[i].weakBindingsSize());
874 printf(" dependents: ");
875 for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex(); dependencyArray[d] != 0xFFFF; ++d) {
876 uint16_t depIndex = dependencyArray[d];
877 if ( depIndex & 0x8000 )
878 printf(" up(%d) ", depIndex & 0x7FFF);
879 else
880 printf(" %d ", depIndex);
881 }
882 printf("\n");
883 printf(" re-exports: ");
884 for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex(); reExportArray[r] != 0xFFFF; ++r)
885 printf(" %d ", reExportArray[r]);
886 printf("\n");
887 }
888 printf("libdyld.dylib:\n");
889 printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr());
890 printf("initializers (count=%u):\n", accelInfo->initializersCount());
891 const dyldCacheAcceleratorInitializer<LittleEndian>* initializers = (dyldCacheAcceleratorInitializer<LittleEndian>*)((char*)accelInfo + accelInfo->initializersOffset());
892 for (uint32_t i=0; i < accelInfo->initializersCount(); ++i) {
893 printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex(), mappings[0].address() + initializers[i].functionOffset());
894 }
895 printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount());
896 const dyldCacheAcceleratorDOFEntry<LittleEndian>* dofs = (dyldCacheAcceleratorDOFEntry<LittleEndian>*)((char*)accelInfo + accelInfo->dofSectionsOffset());
897 for (uint32_t i=0; i < accelInfo->dofSectionsCount(); ++i) {
898 printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex(), dofs[i].sectionAddress(), dofs[i].sectionAddress()+dofs[i].sectionSize());
899 }
900 printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount());
901 const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset());
902 for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) {
903 unsigned imageIndex = bottomUpArray[i];
904 if ( imageIndex < accelInfo->imageExtrasCount() )
905 printf(" image[%3u] %s\n", imageIndex, (char*)options.mappedCache + images[imageIndex].pathFileOffset());
906 else
907 printf(" image[%3u] BAD INDEX\n", imageIndex);
908 }
909 printf("range table (count=%u):\n", accelInfo->rangeTableCount());
910 const dyldCacheAcceleratorRangeEntry<LittleEndian>* rangeTable = (dyldCacheAcceleratorRangeEntry<LittleEndian>*)((char*)accelInfo + accelInfo->rangeTableOffset());
911 for (uint32_t i=0; i < accelInfo->rangeTableCount(); ++i) {
912 const dyldCacheAcceleratorRangeEntry<LittleEndian>& entry = rangeTable[i];
913 printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress(), entry.startAddress() + entry.size(), (char*)options.mappedCache + images[entry.imageIndex()].pathFileOffset());
914 }
915 printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize());
916 const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset();
917 const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize();
918 std::vector<DylibIndexTrie::Entry> dylibEntries;
919 if ( !Trie<DylibIndex>::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) )
920 printf(" malformed dylibs trie\n");
921 for (const DylibIndexTrie::Entry& x : dylibEntries) {
922 printf(" image[%3u] %s\n", x.info.index, x.name.c_str());
923 }
924 }
925 }
926 }
927 else if ( options.mode == modeTextInfo ) {
928 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
929 if ( (header->mappingOffset() < sizeof(dyldCacheHeader<LittleEndian>)) || (header->imagesTextCount() == 0) ) {
930 printf("no text info\n");
931 }
932 else {
933 const dyldCacheImageTextInfo<LittleEndian>* imagesText = (dyldCacheImageTextInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesTextOffset());
934 const dyldCacheImageTextInfo<LittleEndian>* imagesTextEnd = &imagesText[header->imagesTextCount()];
935 printf("dylib text infos (count=%llu):\n", header->imagesTextCount());
936 for (const dyldCacheImageTextInfo<LittleEndian>* p=imagesText; p < imagesTextEnd; ++p) {
937 printf(" 0x%09llX -> 0x%09llX <", p->loadAddress(), p->loadAddress() + p->textSegmentSize());
938 for (int i=0; i<16; ++i) {
939 switch (i) {
940 case 4:
941 case 6:
942 case 8:
943 case 10:
944 printf("-");
945 break;
946 }
947 printf("%02X", p->uuid()[i]);
948 }
949 printf("> %s\n", (char*)options.mappedCache + p->pathOffset());
950 }
951 }
952 }
953 else if ( options.mode == modeLocalSymbols ) {
954 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
955 if ( header->localSymbolsOffset() == 0 ) {
956 fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
957 exit(1);
958 }
959 const bool is64 = (strstr((char*)options.mappedCache, "64") != NULL);
960 const dyldCacheImageInfo<LittleEndian>* imageInfos = (dyldCacheImageInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesOffset());
961 const dyldCacheLocalSymbolsInfo<LittleEndian>* localsInfo = (dyldCacheLocalSymbolsInfo<LittleEndian>*)((char*)options.mappedCache + header->localSymbolsOffset());
962 const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->nlistOffset());
963 const uint32_t nlistCount = localsInfo->nlistCount();
964 const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12;
965 const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->stringsOffset());
966 const uint32_t stringsSize = localsInfo->stringsSize();
967 const uint32_t entriesCount = localsInfo->entriesCount();
968 const dyldCacheLocalSymbolEntry<LittleEndian>* entries = (dyldCacheLocalSymbolEntry<LittleEndian>*)((char*)localsInfo + localsInfo->entriesOffset());
969 printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize);
970 printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize);
971 printf("local symbols by dylib (count=%d):\n", entriesCount);
972 //const char* stringPool = (char*)options.mappedCache + stringsFileOffset;
973 for (int i=0; i < entriesCount; ++i) {
974 const char* imageName = (char*)options.mappedCache + imageInfos[i].pathFileOffset();
975 printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex(), entries[i].nlistCount(), imageName);
976 #if 0
977 if ( is64 ) {
978 const nlist_64* symTab = (nlist_64*)((char*)options.mappedCache + nlistFileOffset);
979 for (int e=0; e < entries[i].nlistCount(); ++e) {
980 const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e];
981 printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]);
982 printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value);
983 }
984 }
985 #endif
986 }
987 }
988 else if ( options.mode == modeStrings ) {
989 if (printStrings) {
990 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
991 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
992 int64_t slide = ma->getSlide();
993 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
994 if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
995 if ( malformedSectionRange ) {
996 stop = true;
997 return;
998 }
999 const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
1000 const char* s = (char*)content;
1001 const char* end = s + info.sectSize;
1002 while ( s < end ) {
1003 printf("%s: %s\n", ma->installName(), s);
1004 while (*s != '\0' )
1005 ++s;
1006 ++s;
1007 }
1008 }
1009 });
1010 });
1011 }
1012
1013 if (printExports) {
1014 dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
1015 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
1016 uint32_t exportTrieRuntimeOffset;
1017 uint32_t exportTrieSize;
1018 if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) {
1019 const uint8_t* start = (uint8_t*)mh + exportTrieRuntimeOffset;
1020 const uint8_t* end = start + exportTrieSize;
1021 std::vector<ExportInfoTrie::Entry> exports;
1022 if ( !ExportInfoTrie::parseTrie(start, end, exports) ) {
1023 return;
1024 }
1025
1026 for (const ExportInfoTrie::Entry& entry: exports) {
1027 printf("%s: %s\n", ma->installName(), entry.name.c_str());
1028 }
1029 }
1030 });
1031 }
1032 }
1033 else if ( options.mode == modeExtract ) {
1034 char pathBuffer[PATH_MAX];
1035 uint32_t bufferSize = PATH_MAX;
1036 if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) {
1037 fprintf(stderr, "Error: could not get path of program\n");
1038 return 1;
1039 }
1040 char* last = strrchr(pathBuffer, '/');
1041 strcpy(last+1, "../../lib/dsc_extractor.bundle");
1042 void* handle = dlopen(pathBuffer, RTLD_LAZY);
1043 if ( handle == NULL ) {
1044 fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer);
1045 return 1;
1046 }
1047
1048 typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
1049 void (^progress)(unsigned current, unsigned total));
1050
1051 extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
1052 if ( proc == NULL ) {
1053 fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
1054 return 1;
1055 }
1056
1057 int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } );
1058 return result;
1059 }
1060 else {
1061 segment_callback_t callback = nullptr;
1062 if ( strcmp((char*)options.mappedCache, "dyld_v1 i386") == 0 ) {
1063 switch ( options.mode ) {
1064 case modeList:
1065 callback = print_list<x86>;
1066 break;
1067 case modeMap:
1068 callback = print_map<x86>;
1069 break;
1070 case modeDependencies:
1071 callback = print_dependencies<x86>;
1072 break;
1073 case modeLinkEdit:
1074 callback = process_linkedit<x86>;
1075 break;
1076 case modeSize:
1077 callback = collect_size<x86>;
1078 break;
1079 case modeNone:
1080 case modeInfo:
1081 case modeSlideInfo:
1082 case modeVerboseSlideInfo:
1083 case modeAcceleratorInfo:
1084 case modeTextInfo:
1085 case modeLocalSymbols:
1086 case modeStrings:
1087 case modeExtract:
1088 break;
1089 }
1090 }
1091 else if ( (strcmp((char*)options.mappedCache, "dyld_v1 x86_64") == 0)
1092 || (strcmp((char*)options.mappedCache, "dyld_v1 x86_64h") == 0) ) {
1093 switch ( options.mode ) {
1094 case modeList:
1095 callback = print_list<x86_64>;
1096 break;
1097 case modeMap:
1098 callback = print_map<x86_64>;
1099 break;
1100 case modeDependencies:
1101 callback = print_dependencies<x86_64>;
1102 break;
1103 case modeLinkEdit:
1104 callback = process_linkedit<x86_64>;
1105 break;
1106 case modeSize:
1107 callback = collect_size<x86_64>;
1108 break;
1109 case modeNone:
1110 case modeInfo:
1111 case modeSlideInfo:
1112 case modeVerboseSlideInfo:
1113 case modeAcceleratorInfo:
1114 case modeTextInfo:
1115 case modeLocalSymbols:
1116 case modeStrings:
1117 case modeExtract:
1118 break;
1119 }
1120 }
1121 else if ( (strncmp((char*)options.mappedCache, "dyld_v1 armv", 14) == 0)
1122 || (strncmp((char*)options.mappedCache, "dyld_v1 armv", 13) == 0)
1123 #if SUPPORT_ARCH_arm64_32
1124 || (strcmp((char*)options.mappedCache, "dyld_v1arm64_32") == 0)
1125 #endif
1126 ) {
1127 switch ( options.mode ) {
1128 case modeList:
1129 callback = print_list<arm>;
1130 break;
1131 case modeMap:
1132 callback = print_map<arm>;
1133 break;
1134 case modeDependencies:
1135 callback = print_dependencies<arm>;
1136 break;
1137 case modeLinkEdit:
1138 callback = process_linkedit<arm>;
1139 break;
1140 case modeSize:
1141 callback = collect_size<arm>;
1142 break;
1143 case modeNone:
1144 case modeInfo:
1145 case modeSlideInfo:
1146 case modeVerboseSlideInfo:
1147 case modeAcceleratorInfo:
1148 case modeTextInfo:
1149 case modeLocalSymbols:
1150 case modeStrings:
1151 case modeExtract:
1152 break;
1153 }
1154 }
1155 else if ( (strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 0)
1156 #if SUPPORT_ARCH_arm64e
1157 || (strcmp((char*)options.mappedCache, "dyld_v1 arm64e") == 0)
1158 #endif
1159 ) {
1160 switch ( options.mode ) {
1161 case modeList:
1162 callback = print_list<arm64>;
1163 break;
1164 case modeMap:
1165 callback = print_map<arm64>;
1166 break;
1167 case modeDependencies:
1168 callback = print_dependencies<arm64>;
1169 break;
1170 case modeLinkEdit:
1171 callback = process_linkedit<arm64>;
1172 break;
1173 case modeSize:
1174 callback = collect_size<arm64>;
1175 break;
1176 case modeNone:
1177 case modeInfo:
1178 case modeSlideInfo:
1179 case modeVerboseSlideInfo:
1180 case modeAcceleratorInfo:
1181 case modeTextInfo:
1182 case modeLocalSymbols:
1183 case modeStrings:
1184 case modeExtract:
1185 break;
1186 }
1187 }
1188 #if SUPPORT_ARCH_arm64_32
1189 else if ( (strcmp((char*)options.mappedCache, "dyld_v1arm64_32") == 0) ) {
1190 switch ( options.mode ) {
1191 case modeList:
1192 callback = print_list<arm64_32>;
1193 break;
1194 case modeMap:
1195 callback = print_map<arm64_32>;
1196 break;
1197 case modeDependencies:
1198 callback = print_dependencies<arm64_32>;
1199 break;
1200 case modeLinkEdit:
1201 callback = process_linkedit<arm64_32>;
1202 break;
1203 case modeSize:
1204 callback = collect_size<arm64_32>;
1205 break;
1206 case modeNone:
1207 case modeInfo:
1208 case modeSlideInfo:
1209 case modeVerboseSlideInfo:
1210 case modeAcceleratorInfo:
1211 case modeTextInfo:
1212 case modeLocalSymbols:
1213 case modeStrings:
1214 case modeExtract:
1215 break;
1216 }
1217 }
1218 #endif
1219 else {
1220 fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
1221 exit(1);
1222 }
1223
1224 __block Results results;
1225 results.dependentTargetFound = false;
1226 int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)cacheLength,
1227 ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) {
1228 (callback)(dylibInfo, segInfo, options, results);
1229 });
1230 if ( iterateResult != 0 ) {
1231 fprintf(stderr, "Error: malformed shared cache file\n");
1232 exit(1);
1233 }
1234
1235 if ( options.mode == modeLinkEdit ) {
1236 // dump -linkedit information
1237 for (std::map<uint32_t, const char*>::iterator it = results.pageToContent.begin(); it != results.pageToContent.end(); ++it) {
1238 printf("0x%08X %s\n", it->first, it->second);
1239 }
1240 }
1241 else if ( options.mode == modeSize ) {
1242 std::sort(results.textSegments.begin(), results.textSegments.end(), TextInfoSorter());
1243 for (std::vector<TextInfo>::iterator it = results.textSegments.begin(); it != results.textSegments.end(); ++it) {
1244 printf(" 0x%08llX %s\n", it->textSize, it->path);
1245 }
1246 }
1247
1248 if ( (options.mode == modeDependencies) && options.dependentsOfPath && !results.dependentTargetFound) {
1249 fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath);
1250 exit(1);
1251 }
1252 }
1253 return 0;
1254 }