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 <fcntl.h>
30#include <stdlib.h>
31#include <errno.h>
32#include <sys/mman.h>
33#include <sys/syslimits.h>
34#include <mach-o/arch.h>
35#include <mach-o/loader.h>
36#include <mach/mach.h>
37
38#include <map>
39#include <vector>
40
41#include "dsc_iterator.h"
42#include "dyld_cache_format.h"
43#include "Architectures.hpp"
44#include "MachOFileAbstraction.hpp"
45#include "CacheFileAbstraction.hpp"
46
47enum Mode {
48	modeNone,
49	modeList,
50	modeMap,
51	modeDependencies,
52	modeSlideInfo,
53	modeLinkEdit,
54	modeInfo,
55	modeSize
56};
57
58struct Options {
59	Mode		mode;
60	const char*	dependentsOfPath;
61	const void*	mappedCache;
62	bool		printUUIDs;
63	bool		printVMAddrs;
64    bool		printDylibVersions;
65	bool		printInodes;
66};
67
68struct TextInfo {
69	uint64_t		textSize;
70	const char*		path;
71};
72
73struct TextInfoSorter {
74	bool operator()(const TextInfo& left, const TextInfo& right) {
75		return (left.textSize > right.textSize);
76	}
77};
78
79struct Results {
80	std::map<uint32_t, const char*>	pageToContent;
81	uint64_t						linkeditBase;
82	bool							dependentTargetFound;
83	std::vector<TextInfo>			textSegments;
84};
85
86
87
88void usage() {
89	fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map [ shared-cache-file ] | -slide_info | -info\n");
90}
91
92#if __x86_64__
93static bool isHaswell()
94{
95	// check system is capable of running x86_64h code
96	struct host_basic_info info;
97	mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
98	mach_port_t hostPort = mach_host_self();
99	kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
100	mach_port_deallocate(mach_task_self(), hostPort);
101	if ( result != KERN_SUCCESS )
102		return false;
103	return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H );
104}
105#endif
106
107/*
108 * Get the path to the native shared cache for this host
109 */
110static const char* default_shared_cache_path() {
111#if __i386__
112	return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386";
113#elif __x86_64__
114	if ( isHaswell() )
115		return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64h";
116	else
117		return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64";
118#elif __ARM_ARCH_5TEJ__
119	return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5";
120#elif __ARM_ARCH_6K__
121	return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6";
122#elif __ARM_ARCH_7K__
123	return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7k";
124#elif __ARM_ARCH_7A__
125	return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7";
126#elif __ARM_ARCH_7F__
127	return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7f";
128#elif __ARM_ARCH_7S__
129	return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7s";
130#elif __arm64__
131	return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64";
132#else
133	#error unsupported architecture
134#endif
135}
136
137typedef void (*segment_callback_t)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
138									const Options& options, Results& results);
139
140
141
142/*
143 * List dependencies from the mach-o header at headerAddr
144 * in the same format as 'otool -L'
145 */
146template <typename A>
147void print_dependencies(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
148																	const Options& options, Results& results) {
149	typedef typename A::P		P;
150	typedef typename A::P::E	E;
151
152	if ( strcmp(options.dependentsOfPath, dylibInfo->path) != 0 )
153		return;
154	if ( strcmp(segInfo->name, "__TEXT") != 0 )
155		return;
156
157	const macho_dylib_command<P>* dylib_cmd;
158	const macho_header<P>* mh = (const macho_header<P>*)dylibInfo->machHeader;
159	const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uintptr_t)dylibInfo->machHeader + sizeof(macho_header<P>));
160	const uint32_t cmd_count = mh->ncmds();
161	const macho_load_command<P>* cmd = cmds;
162	for (uint32_t i = 0; i < cmd_count; ++i) {
163		switch ( cmd->cmd() ) {
164			case LC_LOAD_DYLIB:
165			case LC_ID_DYLIB:
166			case LC_REEXPORT_DYLIB:
167			case LC_LOAD_WEAK_DYLIB:
168			case LC_LOAD_UPWARD_DYLIB:
169				dylib_cmd = (macho_dylib_command<P>*)cmd;
170				if ( options.printDylibVersions ) {
171					uint32_t compat_vers = dylib_cmd->compatibility_version();
172					uint32_t current_vers = dylib_cmd->current_version();
173					printf("\t%s", dylib_cmd->name());
174					if ( compat_vers != 0xFFFFFFFF ) {
175						printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
176						   (compat_vers >> 16),
177						   (compat_vers >> 8) & 0xff,
178						   (compat_vers) & 0xff,
179						   (current_vers >> 16),
180						   (current_vers >> 8) & 0xff,
181						   (current_vers) & 0xff);
182					}
183					else {
184						printf("\n");
185					}
186				}
187				else {
188					printf("\t%s\n", dylib_cmd->name());
189				}
190				break;
191		}
192		cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
193	}
194	results.dependentTargetFound = true;
195}
196
197/*
198 * Print out a dylib from the shared cache, optionally including the UUID or unslid load address
199 */
200template <typename A>
201void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
202																		const Options& options, Results& results)
203{
204	if ( strcmp(segInfo->name, "__TEXT") != 0 )
205		return;
206
207	if ( options.printVMAddrs )
208		printf("0x%08llX ", segInfo->address);
209	if ( options.printInodes )
210		printf("0x%08llX 0x%08llX ", dylibInfo->inode, dylibInfo->modTime);
211	if ( options.printUUIDs ) {
212		if ( dylibInfo->uuid != NULL ) {
213			const uint8_t* uuid = (uint8_t*)dylibInfo->uuid;;
214			printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ",
215    				uuid[0],  uuid[1],  uuid[2],  uuid[3],
216					uuid[4],  uuid[5],  uuid[6],  uuid[7],
217    				uuid[8],  uuid[9],  uuid[10], uuid[11],
218					uuid[12], uuid[13], uuid[14], uuid[15]);
219		}
220	    else
221			printf("<     no uuid in dylib               > ");
222    }
223	if ( dylibInfo->isAlias )
224		printf("[alias] %s\n", dylibInfo->path);
225	else
226		printf("%s\n", dylibInfo->path);
227}
228
229
230template <typename A>
231void collect_size(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
232																		const Options& options, Results& results)
233{
234	if ( strcmp(segInfo->name, "__TEXT") != 0 )
235		return;
236	if ( dylibInfo->isAlias )
237		return;
238
239	TextInfo info;
240	info.textSize = segInfo->fileSize;
241	info.path = dylibInfo->path;
242	results.textSegments.push_back(info);
243	size_t size = segInfo->fileSize;
244}
245
246
247
248
249static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, const char* message, Results& results)
250{
251	for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
252		std::map<uint32_t, const char*>::iterator pos = results.pageToContent.find(p);
253		if ( pos == results.pageToContent.end() ) {
254			results.pageToContent[p] = strdup(message);
255		}
256		else {
257			const char* oldMessage = pos->second;
258			char* newMesssage;
259			asprintf(&newMesssage, "%s, %s", oldMessage, message);
260			results.pageToContent[p] = newMesssage;
261			::free((void*)oldMessage);
262		}
263	}
264}
265
266
267/*
268 * get LINKEDIT info for dylib
269 */
270template <typename A>
271void process_linkedit(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo,
272																			const Options& options, Results& results) {
273	typedef typename A::P		P;
274	typedef typename A::P::E	E;
275	// filter out symlinks
276	if ( dylibInfo->isAlias )
277		return;
278	const macho_header<P>* mh = (const macho_header<P>*)dylibInfo->machHeader;
279	uint32_t ncmds = mh->ncmds();
280	const macho_load_command<P>* const cmds = (macho_load_command<P>*)((long)mh + sizeof(macho_header<P>));
281	const macho_load_command<P>* cmd = cmds;
282	for (uint32_t i = 0; i < ncmds; i++) {
283		if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) {
284			macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
285			char message[1000];
286			const char* shortName = strrchr(dylibInfo->path, '/') + 1;
287			// add export trie info
288			if ( dyldInfo->export_size() != 0 ) {
289				//printf("export_off=0x%X\n", dyldInfo->export_off());
290				uint32_t exportPageOffsetStart = dyldInfo->export_off() & (-4096);
291				uint32_t exportPageOffsetEnd = (dyldInfo->export_off() + dyldInfo->export_size()) & (-4096);
292				sprintf(message, "exports from %s", shortName);
293				add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message, results);
294			}
295			// add binding info
296			if ( dyldInfo->bind_size() != 0 ) {
297				uint32_t bindPageOffsetStart = dyldInfo->bind_off() & (-4096);
298				uint32_t bindPageOffsetEnd = (dyldInfo->bind_off() + dyldInfo->bind_size()) & (-4096);
299				sprintf(message, "bindings from %s", shortName);
300				add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message, results);
301			}
302			// add lazy binding info
303			if ( dyldInfo->lazy_bind_size() != 0 ) {
304				uint32_t lazybindPageOffsetStart = dyldInfo->lazy_bind_off() & (-4096);
305				uint32_t lazybindPageOffsetEnd = (dyldInfo->lazy_bind_off() + dyldInfo->lazy_bind_size()) & (-4096);
306				sprintf(message, "lazy bindings from %s", shortName);
307				add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message, results);
308			}
309			// add weak binding info
310			if ( dyldInfo->weak_bind_size() != 0 ) {
311				uint32_t weakbindPageOffsetStart = dyldInfo->weak_bind_off() & (-4096);
312				uint32_t weakbindPageOffsetEnd = (dyldInfo->weak_bind_off() + dyldInfo->weak_bind_size()) & (-4096);
313				sprintf(message, "weak bindings from %s", shortName);
314				add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message, results);
315			}
316		}
317		cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
318	}
319}
320
321
322/*
323 * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built
324 */
325template <typename A>
326void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, const Options& options, Results& results) {
327	if ( !dylibInfo->isAlias )
328		printf("0x%08llX - 0x%08llX %s %s\n", segInfo->address, segInfo->address + segInfo->fileSize, segInfo->name, dylibInfo->path);
329}
330
331
332static void checkMode(Mode mode) {
333	if ( mode != modeNone ) {
334		fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, or -size\n");
335		usage();
336		exit(1);
337	}
338}
339
340int main (int argc, const char* argv[]) {
341
342	const char* sharedCachePath = default_shared_cache_path();
343
344 	Options options;
345	options.mode = modeNone;
346    options.printUUIDs = false;
347	options.printVMAddrs = false;
348    options.printDylibVersions = false;
349	options.printInodes = false;
350    options.dependentsOfPath = NULL;
351
352    for (uint32_t i = 1; i < argc; i++) {
353        const char* opt = argv[i];
354        if (opt[0] == '-') {
355            if (strcmp(opt, "-list") == 0) {
356				checkMode(options.mode);
357				options.mode = modeList;
358            }
359			else if (strcmp(opt, "-dependents") == 0) {
360				checkMode(options.mode);
361				options.mode = modeDependencies;
362				options.dependentsOfPath = argv[++i];
363                if ( i >= argc ) {
364                    fprintf(stderr, "Error: option -depdendents requires an argument\n");
365                    usage();
366                    exit(1);
367                }
368            }
369			else if (strcmp(opt, "-linkedit") == 0) {
370				checkMode(options.mode);
371				options.mode = modeLinkEdit;
372			}
373			else if (strcmp(opt, "-info") == 0) {
374				checkMode(options.mode);
375				options.mode = modeInfo;
376			}
377			else if (strcmp(opt, "-slide_info") == 0) {
378				checkMode(options.mode);
379				options.mode = modeSlideInfo;
380			}
381			else if (strcmp(opt, "-map") == 0) {
382				checkMode(options.mode);
383				options.mode = modeMap;
384            }
385			else if (strcmp(opt, "-size") == 0) {
386				checkMode(options.mode);
387				options.mode = modeSize;
388            }
389			else if (strcmp(opt, "-uuid") == 0) {
390                options.printUUIDs = true;
391            }
392			else if (strcmp(opt, "-inode") == 0) {
393                options.printInodes = true;
394            }
395			else if (strcmp(opt, "-versions") == 0) {
396                options.printDylibVersions = true;
397            }
398			else if (strcmp(opt, "-vmaddr") == 0) {
399				options.printVMAddrs = true;
400		    }
401			else {
402                fprintf(stderr, "Error: unrecognized option %s\n", opt);
403                usage();
404                exit(1);
405            }
406        }
407		else {
408            sharedCachePath = opt;
409        }
410    }
411
412	if ( options.mode == modeNone ) {
413		fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
414		usage();
415		exit(1);
416	}
417
418	if ( options.mode != modeSlideInfo ) {
419		if ( options.printUUIDs && (options.mode != modeList) )
420			fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
421
422		if ( options.printVMAddrs && (options.mode != modeList) )
423			fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
424
425		if ( options.printDylibVersions && (options.mode != modeDependencies) )
426			fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
427
428		if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) {
429			fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
430			usage();
431			exit(1);
432		}
433	}
434
435	struct stat statbuf;
436	if ( ::stat(sharedCachePath, &statbuf) == -1 ) {
437		fprintf(stderr, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath, errno);
438		exit(1);
439	}
440
441	int cache_fd = ::open(sharedCachePath, O_RDONLY);
442	if ( cache_fd < 0 ) {
443		fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno);
444		exit(1);
445	}
446	options.mappedCache = ::mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
447	if (options.mappedCache == MAP_FAILED) {
448		fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath, errno);
449		exit(1);
450	}
451
452	if ( options.mode == modeSlideInfo ) {
453		const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
454		if ( header->slideInfoOffset() == 0 ) {
455			fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
456			exit(1);
457		}
458		const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
459		const dyldCacheFileMapping<LittleEndian>* dataMapping = &mappings[1];
460		uint64_t dataStartAddress = dataMapping->address();
461		uint64_t dataSize = dataMapping->size();
462		const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)options.mappedCache+header->slideInfoOffset());
463		printf("slide info version=%d\n", slideInfoHeader->version());
464		printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096);
465		const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset());
466		for(int i=0; i < slideInfoHeader->toc_count(); ++i) {
467			printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i));
468			const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)];
469			for(int j=0; j < slideInfoHeader->entries_size(); ++j)
470				printf("%02X", entry->bits[j]);
471			printf("\n");
472		}
473	}
474	else if ( options.mode == modeInfo ) {
475		const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
476		printf("uuid: ");
477		if ( header->mappingOffset() >= 0x68 ) {
478			const uint8_t* uuid = header->uuid();
479			printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
480				   uuid[0],  uuid[1],  uuid[2],  uuid[3],
481				   uuid[4],  uuid[5],  uuid[6],  uuid[7],
482				   uuid[8],  uuid[9],  uuid[10], uuid[11],
483				   uuid[12], uuid[13], uuid[14], uuid[15]);
484		}
485		else {
486			printf("n/a\n");
487		}
488		printf("image count: %u\n", header->imagesCount());
489		printf("mappings:\n");
490		const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
491		for (uint32_t i=0; i < header->mappingCount(); ++i) {
492			if ( mappings[i].init_prot() & VM_PROT_EXECUTE )
493				printf("    __TEXT      %3lluMB,  0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size());
494			else if ( mappings[i]. init_prot() & VM_PROT_WRITE )
495				printf("    __DATA      %3lluMB,  0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size());
496			else if ( mappings[i].init_prot() & VM_PROT_READ )
497				printf("    __LINKEDIT  %3lluMB,  0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size());
498		}
499		if ( header->codeSignatureOffset() != 0 ) {
500			uint64_t size = statbuf.st_size - header->codeSignatureOffset();
501			uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size();
502				printf("    code sign   %3lluMB,  0x%08llX -> 0x%08llX\n", size/(1024*1024), csAddr, csAddr + size);
503		}
504	}
505	else {
506		segment_callback_t callback;
507		if ( strcmp((char*)options.mappedCache, "dyld_v1    i386") == 0 ) {
508			switch ( options.mode ) {
509				case modeList:
510					callback = print_list<x86>;
511					break;
512				case modeMap:
513					callback = print_map<x86>;
514					break;
515				case modeDependencies:
516					callback = print_dependencies<x86>;
517					break;
518				case modeLinkEdit:
519					callback = process_linkedit<x86>;
520					break;
521				case modeSize:
522					callback = collect_size<x86>;
523					break;
524				case modeNone:
525				case modeInfo:
526				case modeSlideInfo:
527					break;
528			}
529		}
530		else if (  (strcmp((char*)options.mappedCache, "dyld_v1  x86_64") == 0)
531				|| (strcmp((char*)options.mappedCache, "dyld_v1 x86_64h") == 0) ) {
532			switch ( options.mode ) {
533				case modeList:
534					callback = print_list<x86_64>;
535					break;
536				case modeMap:
537					callback = print_map<x86_64>;
538					break;
539				case modeDependencies:
540					callback = print_dependencies<x86_64>;
541					break;
542				case modeLinkEdit:
543					callback = process_linkedit<x86_64>;
544					break;
545				case modeSize:
546					callback = collect_size<x86_64>;
547					break;
548				case modeNone:
549				case modeInfo:
550				case modeSlideInfo:
551					break;
552			}
553		}
554		else if ( (strncmp((char*)options.mappedCache, "dyld_v1   armv", 14) == 0)
555			   || (strncmp((char*)options.mappedCache, "dyld_v1  armv", 13) == 0) ) {
556			switch ( options.mode ) {
557				case modeList:
558					callback = print_list<arm>;
559					break;
560				case modeMap:
561					callback = print_map<arm>;
562					break;
563				case modeDependencies:
564					callback = print_dependencies<arm>;
565					break;
566				case modeLinkEdit:
567					callback = process_linkedit<arm>;
568					break;
569				case modeSize:
570					callback = collect_size<arm>;
571					break;
572				case modeNone:
573				case modeInfo:
574				case modeSlideInfo:
575					break;
576			}
577		}
578		else if ( strcmp((char*)options.mappedCache, "dyld_v1   arm64") == 0 ) {
579			switch ( options.mode ) {
580				case modeList:
581					callback = print_list<arm64>;
582					break;
583				case modeMap:
584					callback = print_map<arm64>;
585					break;
586				case modeDependencies:
587					callback = print_dependencies<arm64>;
588					break;
589				case modeLinkEdit:
590					callback = process_linkedit<arm64>;
591					break;
592				case modeSize:
593					callback = collect_size<arm64>;
594					break;
595				case modeNone:
596				case modeInfo:
597				case modeSlideInfo:
598					break;
599			}
600		}
601 		else {
602			fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
603			exit(1);
604		}
605
606		__block Results results;
607		results.dependentTargetFound = false;
608		int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)statbuf.st_size,
609										   ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) {
610											   (callback)(dylibInfo, segInfo, options, results);
611										   });
612		if ( iterateResult != 0 ) {
613			fprintf(stderr, "Error: malformed shared cache file\n");
614			exit(1);
615		}
616
617		if ( options.mode == modeLinkEdit ) {
618			// dump -linkedit information
619			for (std::map<uint32_t, const char*>::iterator it = results.pageToContent.begin(); it != results.pageToContent.end(); ++it) {
620				printf("0x%08X %s\n", it->first, it->second);
621			}
622		}
623		else if ( options.mode == modeSize ) {
624			std::sort(results.textSegments.begin(), results.textSegments.end(), TextInfoSorter());
625			for (std::vector<TextInfo>::iterator it = results.textSegments.begin(); it != results.textSegments.end(); ++it) {
626				printf(" 0x%08llX  %s\n", it->textSize, it->path);
627			}
628		}
629
630		if ( (options.mode == modeDependencies) && options.dependentsOfPath && !results.dependentTargetFound) {
631			fprintf(stderr, "Error: could not find '%s' in the shared cache at\n  %s\n", options.dependentsOfPath, sharedCachePath);
632			exit(1);
633		}
634	}
635	return 0;
636}
637