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