]> git.saurik.com Git - apple/dyld.git/blob - launch-cache/dyld_shared_cache_util.cpp
63d6b733f83ac278a349b536314ff0c65caaae5e
[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/mach.h>
39
40 #include <map>
41 #include <vector>
42
43 #include "dsc_iterator.h"
44 #include "dsc_extractor.h"
45 #include "dyld_cache_format.h"
46 #include "Architectures.hpp"
47 #include "MachOFileAbstraction.hpp"
48 #include "CacheFileAbstraction.hpp"
49 #include "Trie.hpp"
50
51 enum Mode {
52 modeNone,
53 modeList,
54 modeMap,
55 modeDependencies,
56 modeSlideInfo,
57 modeAcceleratorInfo,
58 modeTextInfo,
59 modeLinkEdit,
60 modeLocalSymbols,
61 modeInfo,
62 modeSize,
63 modeExtract
64 };
65
66 struct Options {
67 Mode mode;
68 const char* dependentsOfPath;
69 const void* mappedCache;
70 const char* extractionDir;
71 bool printUUIDs;
72 bool printVMAddrs;
73 bool printDylibVersions;
74 bool printInodes;
75 };
76
77 struct TextInfo {
78 uint64_t textSize;
79 const char* path;
80 };
81
82 struct TextInfoSorter {
83 bool operator()(const TextInfo& left, const TextInfo& right) {
84 return (left.textSize > right.textSize);
85 }
86 };
87
88 struct Results {
89 std::map<uint32_t, const char*> pageToContent;
90 uint64_t linkeditBase;
91 bool dependentTargetFound;
92 std::vector<TextInfo> textSegments;
93 };
94
95
96
97 void usage() {
98 fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map | -slide_info | -info | -extract <dylib-dir> [ shared-cache-file ] \n");
99 }
100
101 #if __x86_64__
102 static bool isHaswell()
103 {
104 // check system is capable of running x86_64h code
105 struct host_basic_info info;
106 mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
107 mach_port_t hostPort = mach_host_self();
108 kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
109 mach_port_deallocate(mach_task_self(), hostPort);
110 if ( result != KERN_SUCCESS )
111 return false;
112 return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H );
113 }
114 #endif
115
116 /*
117 * Get the path to the native shared cache for this host
118 */
119 static const char* default_shared_cache_path() {
120 #if __i386__
121 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386";
122 #elif __x86_64__
123 if ( isHaswell() )
124 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64h";
125 else
126 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64";
127 #elif __ARM_ARCH_5TEJ__
128 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5";
129 #elif __ARM_ARCH_6K__
130 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6";
131 #elif __ARM_ARCH_7K__
132 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7k";
133 #elif __ARM_ARCH_7A__
134 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7";
135 #elif __ARM_ARCH_7F__
136 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7f";
137 #elif __ARM_ARCH_7S__
138 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7s";
139 #elif __arm64__
140 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64";
141 #else
142 #error unsupported architecture
143 #endif
144 }
145
146 typedef void (*segment_callback_t)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
147 const Options& options, Results& results);
148
149
150
151 /*
152 * List dependencies from the mach-o header at headerAddr
153 * in the same format as 'otool -L'
154 */
155 template <typename A>
156 void print_dependencies(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
157 const Options& options, Results& results) {
158 typedef typename A::P P;
159 typedef typename A::P::E E;
160
161 if ( strcmp(options.dependentsOfPath, dylibInfo->path) != 0 )
162 return;
163 if ( strcmp(segInfo->name, "__TEXT") != 0 )
164 return;
165
166 const macho_dylib_command<P>* dylib_cmd;
167 const macho_header<P>* mh = (const macho_header<P>*)dylibInfo->machHeader;
168 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uintptr_t)dylibInfo->machHeader + sizeof(macho_header<P>));
169 const uint32_t cmd_count = mh->ncmds();
170 const macho_load_command<P>* cmd = cmds;
171 for (uint32_t i = 0; i < cmd_count; ++i) {
172 switch ( cmd->cmd() ) {
173 case LC_LOAD_DYLIB:
174 case LC_ID_DYLIB:
175 case LC_REEXPORT_DYLIB:
176 case LC_LOAD_WEAK_DYLIB:
177 case LC_LOAD_UPWARD_DYLIB:
178 dylib_cmd = (macho_dylib_command<P>*)cmd;
179 if ( options.printDylibVersions ) {
180 uint32_t compat_vers = dylib_cmd->compatibility_version();
181 uint32_t current_vers = dylib_cmd->current_version();
182 printf("\t%s", dylib_cmd->name());
183 if ( compat_vers != 0xFFFFFFFF ) {
184 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
185 (compat_vers >> 16),
186 (compat_vers >> 8) & 0xff,
187 (compat_vers) & 0xff,
188 (current_vers >> 16),
189 (current_vers >> 8) & 0xff,
190 (current_vers) & 0xff);
191 }
192 else {
193 printf("\n");
194 }
195 }
196 else {
197 printf("\t%s\n", dylib_cmd->name());
198 }
199 break;
200 }
201 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
202 }
203 results.dependentTargetFound = true;
204 }
205
206 /*
207 * Print out a dylib from the shared cache, optionally including the UUID or unslid load address
208 */
209 template <typename A>
210 void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
211 const Options& options, Results& results)
212 {
213 if ( strcmp(segInfo->name, "__TEXT") != 0 )
214 return;
215
216 if ( options.printVMAddrs )
217 printf("0x%08llX ", segInfo->address);
218 if ( options.printInodes )
219 printf("0x%08llX 0x%08llX ", dylibInfo->inode, dylibInfo->modTime);
220 if ( options.printUUIDs ) {
221 if ( dylibInfo->uuid != NULL ) {
222 const uint8_t* uuid = (uint8_t*)dylibInfo->uuid;;
223 printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ",
224 uuid[0], uuid[1], uuid[2], uuid[3],
225 uuid[4], uuid[5], uuid[6], uuid[7],
226 uuid[8], uuid[9], uuid[10], uuid[11],
227 uuid[12], uuid[13], uuid[14], uuid[15]);
228 }
229 else
230 printf("< no uuid in dylib > ");
231 }
232 if ( dylibInfo->isAlias )
233 printf("[alias] %s\n", dylibInfo->path);
234 else
235 printf("%s\n", dylibInfo->path);
236 }
237
238
239 template <typename A>
240 void collect_size(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
241 const Options& options, Results& results)
242 {
243 if ( strcmp(segInfo->name, "__TEXT") != 0 )
244 return;
245 if ( dylibInfo->isAlias )
246 return;
247
248 TextInfo info;
249 info.textSize = segInfo->fileSize;
250 info.path = dylibInfo->path;
251 results.textSegments.push_back(info);
252 size_t size = segInfo->fileSize;
253 }
254
255
256
257
258 static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, const char* message, Results& results)
259 {
260 for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
261 std::map<uint32_t, const char*>::iterator pos = results.pageToContent.find(p);
262 if ( pos == results.pageToContent.end() ) {
263 results.pageToContent[p] = strdup(message);
264 }
265 else {
266 const char* oldMessage = pos->second;
267 char* newMesssage;
268 asprintf(&newMesssage, "%s, %s", oldMessage, message);
269 results.pageToContent[p] = newMesssage;
270 ::free((void*)oldMessage);
271 }
272 }
273 }
274
275
276 /*
277 * get LINKEDIT info for dylib
278 */
279 template <typename A>
280 void process_linkedit(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
281 const Options& options, Results& results) {
282 typedef typename A::P P;
283 typedef typename A::P::E E;
284 // filter out symlinks
285 if ( dylibInfo->isAlias )
286 return;
287 const macho_header<P>* mh = (const macho_header<P>*)dylibInfo->machHeader;
288 uint32_t ncmds = mh->ncmds();
289 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((long)mh + sizeof(macho_header<P>));
290 const macho_load_command<P>* cmd = cmds;
291 for (uint32_t i = 0; i < ncmds; i++) {
292 if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) {
293 macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
294 char message[1000];
295 const char* shortName = strrchr(dylibInfo->path, '/') + 1;
296 // add export trie info
297 if ( dyldInfo->export_size() != 0 ) {
298 //printf("export_off=0x%X\n", dyldInfo->export_off());
299 uint32_t exportPageOffsetStart = dyldInfo->export_off() & (-4096);
300 uint32_t exportPageOffsetEnd = (dyldInfo->export_off() + dyldInfo->export_size()) & (-4096);
301 sprintf(message, "exports from %s", shortName);
302 add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message, results);
303 }
304 // add binding info
305 if ( dyldInfo->bind_size() != 0 ) {
306 uint32_t bindPageOffsetStart = dyldInfo->bind_off() & (-4096);
307 uint32_t bindPageOffsetEnd = (dyldInfo->bind_off() + dyldInfo->bind_size()) & (-4096);
308 sprintf(message, "bindings from %s", shortName);
309 add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message, results);
310 }
311 // add lazy binding info
312 if ( dyldInfo->lazy_bind_size() != 0 ) {
313 uint32_t lazybindPageOffsetStart = dyldInfo->lazy_bind_off() & (-4096);
314 uint32_t lazybindPageOffsetEnd = (dyldInfo->lazy_bind_off() + dyldInfo->lazy_bind_size()) & (-4096);
315 sprintf(message, "lazy bindings from %s", shortName);
316 add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message, results);
317 }
318 // add weak binding info
319 if ( dyldInfo->weak_bind_size() != 0 ) {
320 uint32_t weakbindPageOffsetStart = dyldInfo->weak_bind_off() & (-4096);
321 uint32_t weakbindPageOffsetEnd = (dyldInfo->weak_bind_off() + dyldInfo->weak_bind_size()) & (-4096);
322 sprintf(message, "weak bindings from %s", shortName);
323 add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message, results);
324 }
325 }
326 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
327 }
328 }
329
330
331 /*
332 * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built
333 */
334 template <typename A>
335 void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, const Options& options, Results& results) {
336 if ( !dylibInfo->isAlias )
337 printf("0x%08llX - 0x%08llX %s %s\n", segInfo->address, segInfo->address + segInfo->fileSize, segInfo->name, dylibInfo->path);
338 }
339
340
341 static void checkMode(Mode mode) {
342 if ( mode != modeNone ) {
343 fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, -extract, or -size\n");
344 usage();
345 exit(1);
346 }
347 }
348
349 int main (int argc, const char* argv[]) {
350
351 const char* sharedCachePath = default_shared_cache_path();
352
353 Options options;
354 options.mode = modeNone;
355 options.printUUIDs = false;
356 options.printVMAddrs = false;
357 options.printDylibVersions = false;
358 options.printInodes = false;
359 options.dependentsOfPath = NULL;
360 options.extractionDir = NULL;
361
362 for (uint32_t i = 1; i < argc; i++) {
363 const char* opt = argv[i];
364 if (opt[0] == '-') {
365 if (strcmp(opt, "-list") == 0) {
366 checkMode(options.mode);
367 options.mode = modeList;
368 }
369 else if (strcmp(opt, "-dependents") == 0) {
370 checkMode(options.mode);
371 options.mode = modeDependencies;
372 options.dependentsOfPath = argv[++i];
373 if ( i >= argc ) {
374 fprintf(stderr, "Error: option -depdendents requires an argument\n");
375 usage();
376 exit(1);
377 }
378 }
379 else if (strcmp(opt, "-linkedit") == 0) {
380 checkMode(options.mode);
381 options.mode = modeLinkEdit;
382 }
383 else if (strcmp(opt, "-info") == 0) {
384 checkMode(options.mode);
385 options.mode = modeInfo;
386 }
387 else if (strcmp(opt, "-slide_info") == 0) {
388 checkMode(options.mode);
389 options.mode = modeSlideInfo;
390 }
391 else if (strcmp(opt, "-accelerator_info") == 0) {
392 checkMode(options.mode);
393 options.mode = modeAcceleratorInfo;
394 }
395 else if (strcmp(opt, "-text_info") == 0) {
396 checkMode(options.mode);
397 options.mode = modeTextInfo;
398 }
399 else if (strcmp(opt, "-local_symbols") == 0) {
400 checkMode(options.mode);
401 options.mode = modeLocalSymbols;
402 }
403 else if (strcmp(opt, "-map") == 0) {
404 checkMode(options.mode);
405 options.mode = modeMap;
406 }
407 else if (strcmp(opt, "-size") == 0) {
408 checkMode(options.mode);
409 options.mode = modeSize;
410 }
411 else if (strcmp(opt, "-extract") == 0) {
412 checkMode(options.mode);
413 options.mode = modeExtract;
414 options.extractionDir = argv[++i];
415 if ( i >= argc ) {
416 fprintf(stderr, "Error: option -extract requires a directory argument\n");
417 usage();
418 exit(1);
419 }
420 }
421 else if (strcmp(opt, "-uuid") == 0) {
422 options.printUUIDs = true;
423 }
424 else if (strcmp(opt, "-inode") == 0) {
425 options.printInodes = true;
426 }
427 else if (strcmp(opt, "-versions") == 0) {
428 options.printDylibVersions = true;
429 }
430 else if (strcmp(opt, "-vmaddr") == 0) {
431 options.printVMAddrs = true;
432 }
433 else {
434 fprintf(stderr, "Error: unrecognized option %s\n", opt);
435 usage();
436 exit(1);
437 }
438 }
439 else {
440 sharedCachePath = opt;
441 }
442 }
443
444 if ( options.mode == modeNone ) {
445 fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
446 usage();
447 exit(1);
448 }
449
450 if ( options.mode != modeSlideInfo ) {
451 if ( options.printUUIDs && (options.mode != modeList) )
452 fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
453
454 if ( options.printVMAddrs && (options.mode != modeList) )
455 fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
456
457 if ( options.printDylibVersions && (options.mode != modeDependencies) )
458 fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
459
460 if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) {
461 fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
462 usage();
463 exit(1);
464 }
465 }
466
467 struct stat statbuf;
468 if ( ::stat(sharedCachePath, &statbuf) == -1 ) {
469 fprintf(stderr, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath, errno);
470 exit(1);
471 }
472
473 int cache_fd = ::open(sharedCachePath, O_RDONLY);
474 if ( cache_fd < 0 ) {
475 fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno);
476 exit(1);
477 }
478 options.mappedCache = ::mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
479 if (options.mappedCache == MAP_FAILED) {
480 fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath, errno);
481 exit(1);
482 }
483
484 if ( options.mode == modeSlideInfo ) {
485 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
486 if ( header->slideInfoOffset() == 0 ) {
487 fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
488 exit(1);
489 }
490 const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
491 const dyldCacheFileMapping<LittleEndian>* dataMapping = &mappings[1];
492 uint64_t dataStartAddress = dataMapping->address();
493 uint64_t dataSize = dataMapping->size();
494 const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)options.mappedCache+header->slideInfoOffset());
495 printf("slide info version=%d\n", slideInfoHeader->version());
496 if ( slideInfoHeader->version() == 1 ) {
497 printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096);
498 const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset());
499 for(int i=0; i < slideInfoHeader->toc_count(); ++i) {
500 printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i));
501 const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)];
502 for(int j=0; j < slideInfoHeader->entries_size(); ++j)
503 printf("%02X", entry->bits[j]);
504 printf("\n");
505 }
506 }
507 else if ( slideInfoHeader->version() == 2 ) {
508 const dyldCacheSlideInfo2<LittleEndian>* slideInfo = (dyldCacheSlideInfo2<LittleEndian>*)(slideInfoHeader);
509 printf("page_size=%d\n", slideInfo->page_size());
510 printf("delta_mask=0x%016llX\n", slideInfo->delta_mask());
511 printf("value_add=0x%016llX\n", slideInfo->value_add());
512 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count(), slideInfo->page_extras_count());
513 const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset());
514 const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset());
515 for (int i=0; i < slideInfo->page_starts_count(); ++i) {
516 const uint16_t start = starts[i];
517 if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
518 printf("page[% 5d]: no rebasing\n", i);
519 }
520 else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
521 printf("page[% 5d]: ", i);
522 int j=(start & 0x3FFF);
523 bool done = false;
524 do {
525 uint16_t aStart = extras[j];
526 printf("start=0x%04X ", aStart & 0x3FFF);
527 done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
528 ++j;
529 } while ( !done );
530 printf("\n");
531 }
532 else {
533 printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
534 }
535 }
536 }
537 }
538 else if ( options.mode == modeInfo ) {
539 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
540 printf("uuid: ");
541 if ( header->mappingOffset() >= 0x68 ) {
542 const uint8_t* uuid = header->uuid();
543 printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
544 uuid[0], uuid[1], uuid[2], uuid[3],
545 uuid[4], uuid[5], uuid[6], uuid[7],
546 uuid[8], uuid[9], uuid[10], uuid[11],
547 uuid[12], uuid[13], uuid[14], uuid[15]);
548 }
549 else {
550 printf("n/a\n");
551 }
552 printf("image count: %u\n", header->imagesCount());
553 if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) {
554 printf("branch pool count: %u\n", header->branchPoolsCount());
555 }
556 printf("mappings:\n");
557 const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
558 for (uint32_t i=0; i < header->mappingCount(); ++i) {
559 if ( mappings[i].init_prot() & VM_PROT_EXECUTE )
560 printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
561 mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
562 mappings[i].address(), mappings[i].address() + mappings[i].size());
563 else if ( mappings[i]. init_prot() & VM_PROT_WRITE )
564 printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
565 mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
566 mappings[i].address(), mappings[i].address() + mappings[i].size());
567 else if ( mappings[i].init_prot() & VM_PROT_READ )
568 printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
569 mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(),
570 mappings[i].address(), mappings[i].address() + mappings[i].size());
571 }
572 if ( header->codeSignatureOffset() != 0 ) {
573 uint64_t size = statbuf.st_size - header->codeSignatureOffset();
574 uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size();
575 if ( size != 0 )
576 printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
577 size/(1024*1024), header->codeSignatureOffset(), header->codeSignatureOffset() + size, csAddr, csAddr + size);
578 }
579 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
580 header->slideInfoSize()/1024, header->slideInfoOffset(), header->slideInfoOffset() + header->slideInfoSize());
581 if ( header->localSymbolsOffset() != 0 )
582 printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
583 header->localSymbolsSize()/(1024*1024), header->localSymbolsOffset(), header->localSymbolsOffset() + header->localSymbolsSize());
584 if ( (header->mappingOffset() >= 0x78) && (header->accelerateInfoSize() != 0) )
585 printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n",
586 header->accelerateInfoSize()/1024, header->accelerateInfoAddr(), header->accelerateInfoAddr() + header->accelerateInfoSize());
587 }
588 else if ( options.mode == modeAcceleratorInfo ) {
589 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
590 if ( (header->mappingOffset() < sizeof(dyldCacheHeader<LittleEndian>)) || (header->accelerateInfoSize() == 0) ) {
591 printf("no accelerator info\n");
592 }
593 else {
594 const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
595 uint64_t aiAddr = header->accelerateInfoAddr();
596 dyldCacheAcceleratorInfo<LittleEndian>* accelInfo = NULL;
597 for (uint32_t i=0; i < header->mappingCount(); ++i) {
598 if ( (mappings[i].address() <= aiAddr) && (aiAddr < mappings[i].address()+mappings[i].size()) ) {
599 uint64_t offset = aiAddr - mappings[i].address() + mappings[i].file_offset();
600 accelInfo = (dyldCacheAcceleratorInfo<LittleEndian>*)((uint8_t*)options.mappedCache + offset);
601 }
602 }
603 if ( accelInfo == NULL ) {
604 printf("accelerator info not in any mapped range\n");
605 }
606 else {
607 const dyldCacheImageInfo<LittleEndian>* images = (dyldCacheImageInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesOffset());
608 const dyldCacheImageInfoExtra<LittleEndian>* imagesExtra = (dyldCacheImageInfoExtra<LittleEndian>*)((char*)accelInfo + accelInfo->imagesExtrasOffset());
609 const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset());
610 const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset());
611 printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount());
612 for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) {
613 printf(" image[%3u] %s:\n", i, (char*)options.mappedCache +images[i].pathFileOffset());
614 printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr(), imagesExtra[i].exportsTrieSize());
615 if ( imagesExtra[i].weakBindingsSize() )
616 printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr(), imagesExtra[i].weakBindingsSize());
617 printf(" dependents: ");
618 for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex(); dependencyArray[d] != 0xFFFF; ++d) {
619 uint16_t depIndex = dependencyArray[d];
620 if ( depIndex & 0x8000 )
621 printf(" up(%d) ", depIndex & 0x7FFF);
622 else
623 printf(" %d ", depIndex);
624 }
625 printf("\n");
626 printf(" re-exports: ");
627 for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex(); reExportArray[r] != 0xFFFF; ++r)
628 printf(" %d ", reExportArray[r]);
629 printf("\n");
630 }
631 printf("libdyld.dylib:\n");
632 printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr());
633 printf("initializers (count=%u):\n", accelInfo->initializersCount());
634 const dyldCacheAcceleratorInitializer<LittleEndian>* initializers = (dyldCacheAcceleratorInitializer<LittleEndian>*)((char*)accelInfo + accelInfo->initializersOffset());
635 for (uint32_t i=0; i < accelInfo->initializersCount(); ++i) {
636 printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex(), mappings[0].address() + initializers[i].functionOffset());
637 }
638 printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount());
639 const dyldCacheAcceleratorDOFEntry<LittleEndian>* dofs = (dyldCacheAcceleratorDOFEntry<LittleEndian>*)((char*)accelInfo + accelInfo->dofSectionsOffset());
640 for (uint32_t i=0; i < accelInfo->dofSectionsCount(); ++i) {
641 printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex(), dofs[i].sectionAddress(), dofs[i].sectionAddress()+dofs[i].sectionSize());
642 }
643 printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount());
644 const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset());
645 for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) {
646 unsigned imageIndex = bottomUpArray[i];
647 if ( imageIndex < accelInfo->imageExtrasCount() )
648 printf(" image[%3u] %s\n", imageIndex, (char*)options.mappedCache + images[imageIndex].pathFileOffset());
649 else
650 printf(" image[%3u] BAD INDEX\n", imageIndex);
651 }
652 printf("range table (count=%u):\n", accelInfo->rangeTableCount());
653 const dyldCacheAcceleratorRangeEntry<LittleEndian>* rangeTable = (dyldCacheAcceleratorRangeEntry<LittleEndian>*)((char*)accelInfo + accelInfo->rangeTableOffset());
654 for (uint32_t i=0; i < accelInfo->rangeTableCount(); ++i) {
655 const dyldCacheAcceleratorRangeEntry<LittleEndian>& entry = rangeTable[i];
656 printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress(), entry.startAddress() + entry.size(), (char*)options.mappedCache + images[entry.imageIndex()].pathFileOffset());
657 }
658 printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize());
659 const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset();
660 const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize();
661 std::vector<DylibIndexTrie::Entry> dylibEntries;
662 if ( !Trie<DylibIndex>::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) )
663 printf(" malformed dylibs trie\n");
664 for (const DylibIndexTrie::Entry& x : dylibEntries) {
665 printf(" image[%3u] %s\n", x.info.index, x.name.c_str());
666 }
667 }
668 }
669 }
670 else if ( options.mode == modeTextInfo ) {
671 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
672 if ( (header->mappingOffset() < sizeof(dyldCacheHeader<LittleEndian>)) || (header->imagesTextCount() == 0) ) {
673 printf("no text info\n");
674 }
675 else {
676 const dyldCacheImageTextInfo<LittleEndian>* imagesText = (dyldCacheImageTextInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesTextOffset());
677 const dyldCacheImageTextInfo<LittleEndian>* imagesTextEnd = &imagesText[header->imagesTextCount()];
678 printf("dylib text infos (count=%llu):\n", header->imagesTextCount());
679 for (const dyldCacheImageTextInfo<LittleEndian>* p=imagesText; p < imagesTextEnd; ++p) {
680 printf(" 0x%09llX -> 0x%09llX <", p->loadAddress(), p->loadAddress() + p->textSegmentSize());
681 for (int i=0; i<16; ++i) {
682 switch (i) {
683 case 4:
684 case 6:
685 case 8:
686 case 10:
687 printf("-");
688 break;
689 }
690 printf("%02X", p->uuid()[i]);
691 }
692 printf("> %s\n", (char*)options.mappedCache + p->pathOffset());
693 }
694 }
695 }
696 else if ( options.mode == modeLocalSymbols ) {
697 const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
698 if ( header->localSymbolsOffset() == 0 ) {
699 fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
700 exit(1);
701 }
702 const bool is64 = (strstr((char*)options.mappedCache, "64") != NULL);
703 const dyldCacheImageInfo<LittleEndian>* imageInfos = (dyldCacheImageInfo<LittleEndian>*)((char*)options.mappedCache + header->imagesOffset());
704 const dyldCacheLocalSymbolsInfo<LittleEndian>* localsInfo = (dyldCacheLocalSymbolsInfo<LittleEndian>*)((char*)options.mappedCache + header->localSymbolsOffset());
705 const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->nlistOffset());
706 const uint32_t nlistCount = localsInfo->nlistCount();
707 const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12;
708 const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->stringsOffset());
709 const uint32_t stringsSize = localsInfo->stringsSize();
710 const uint32_t entriesCount = localsInfo->entriesCount();
711 const dyldCacheLocalSymbolEntry<LittleEndian>* entries = (dyldCacheLocalSymbolEntry<LittleEndian>*)((char*)localsInfo + localsInfo->entriesOffset());
712 printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize);
713 printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize);
714 printf("local symbols by dylib (count=%d):\n", entriesCount);
715 const char* stringPool = (char*)options.mappedCache + stringsFileOffset;
716 for (int i=0; i < entriesCount; ++i) {
717 const char* imageName = (char*)options.mappedCache + imageInfos[i].pathFileOffset();
718 printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex(), entries[i].nlistCount(), imageName);
719 #if 0
720 if ( is64 ) {
721 const nlist_64* symTab = (nlist_64*)((char*)options.mappedCache + nlistFileOffset);
722 for (int e=0; e < entries[i].nlistCount(); ++e) {
723 const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e];
724 printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]);
725 printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value);
726 }
727 }
728 #endif
729 }
730 }
731 else if ( options.mode == modeExtract ) {
732 char pathBuffer[PATH_MAX];
733 uint32_t bufferSize = PATH_MAX;
734 if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) {
735 fprintf(stderr, "Error: could not get path of program\n");
736 return 1;
737 }
738 char* last = strrchr(pathBuffer, '/');
739 strcpy(last+1, "../../lib/dsc_extractor.bundle");
740 void* handle = dlopen(pathBuffer, RTLD_LAZY);
741 if ( handle == NULL ) {
742 fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer);
743 return 1;
744 }
745
746 typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
747 void (^progress)(unsigned current, unsigned total));
748
749 extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
750 if ( proc == NULL ) {
751 fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
752 return 1;
753 }
754
755 int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } );
756 return result;
757 }
758 else {
759 segment_callback_t callback;
760 if ( strcmp((char*)options.mappedCache, "dyld_v1 i386") == 0 ) {
761 switch ( options.mode ) {
762 case modeList:
763 callback = print_list<x86>;
764 break;
765 case modeMap:
766 callback = print_map<x86>;
767 break;
768 case modeDependencies:
769 callback = print_dependencies<x86>;
770 break;
771 case modeLinkEdit:
772 callback = process_linkedit<x86>;
773 break;
774 case modeSize:
775 callback = collect_size<x86>;
776 break;
777 case modeNone:
778 case modeInfo:
779 case modeSlideInfo:
780 case modeAcceleratorInfo:
781 case modeTextInfo:
782 case modeLocalSymbols:
783 case modeExtract:
784 break;
785 }
786 }
787 else if ( (strcmp((char*)options.mappedCache, "dyld_v1 x86_64") == 0)
788 || (strcmp((char*)options.mappedCache, "dyld_v1 x86_64h") == 0) ) {
789 switch ( options.mode ) {
790 case modeList:
791 callback = print_list<x86_64>;
792 break;
793 case modeMap:
794 callback = print_map<x86_64>;
795 break;
796 case modeDependencies:
797 callback = print_dependencies<x86_64>;
798 break;
799 case modeLinkEdit:
800 callback = process_linkedit<x86_64>;
801 break;
802 case modeSize:
803 callback = collect_size<x86_64>;
804 break;
805 case modeNone:
806 case modeInfo:
807 case modeSlideInfo:
808 case modeAcceleratorInfo:
809 case modeTextInfo:
810 case modeLocalSymbols:
811 case modeExtract:
812 break;
813 }
814 }
815 else if ( (strncmp((char*)options.mappedCache, "dyld_v1 armv", 14) == 0)
816 || (strncmp((char*)options.mappedCache, "dyld_v1 armv", 13) == 0) ) {
817 switch ( options.mode ) {
818 case modeList:
819 callback = print_list<arm>;
820 break;
821 case modeMap:
822 callback = print_map<arm>;
823 break;
824 case modeDependencies:
825 callback = print_dependencies<arm>;
826 break;
827 case modeLinkEdit:
828 callback = process_linkedit<arm>;
829 break;
830 case modeSize:
831 callback = collect_size<arm>;
832 break;
833 case modeNone:
834 case modeInfo:
835 case modeSlideInfo:
836 case modeAcceleratorInfo:
837 case modeTextInfo:
838 case modeLocalSymbols:
839 case modeExtract:
840 break;
841 }
842 }
843 else if ( strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 0 ) {
844 switch ( options.mode ) {
845 case modeList:
846 callback = print_list<arm64>;
847 break;
848 case modeMap:
849 callback = print_map<arm64>;
850 break;
851 case modeDependencies:
852 callback = print_dependencies<arm64>;
853 break;
854 case modeLinkEdit:
855 callback = process_linkedit<arm64>;
856 break;
857 case modeSize:
858 callback = collect_size<arm64>;
859 break;
860 case modeNone:
861 case modeInfo:
862 case modeSlideInfo:
863 case modeAcceleratorInfo:
864 case modeTextInfo:
865 case modeLocalSymbols:
866 case modeExtract:
867 break;
868 }
869 }
870 else {
871 fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
872 exit(1);
873 }
874
875 __block Results results;
876 results.dependentTargetFound = false;
877 int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)statbuf.st_size,
878 ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) {
879 (callback)(dylibInfo, segInfo, options, results);
880 });
881 if ( iterateResult != 0 ) {
882 fprintf(stderr, "Error: malformed shared cache file\n");
883 exit(1);
884 }
885
886 if ( options.mode == modeLinkEdit ) {
887 // dump -linkedit information
888 for (std::map<uint32_t, const char*>::iterator it = results.pageToContent.begin(); it != results.pageToContent.end(); ++it) {
889 printf("0x%08X %s\n", it->first, it->second);
890 }
891 }
892 else if ( options.mode == modeSize ) {
893 std::sort(results.textSegments.begin(), results.textSegments.end(), TextInfoSorter());
894 for (std::vector<TextInfo>::iterator it = results.textSegments.begin(); it != results.textSegments.end(); ++it) {
895 printf(" 0x%08llX %s\n", it->textSize, it->path);
896 }
897 }
898
899 if ( (options.mode == modeDependencies) && options.dependentsOfPath && !results.dependentTargetFound) {
900 fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath);
901 exit(1);
902 }
903 }
904 return 0;
905 }