1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
33 #include <sys/syslimits.h>
34 #include <mach-o/arch.h>
35 #include <mach-o/loader.h>
39 #include "dsc_iterator.h"
40 #include "dyld_cache_format.h"
41 #include "Architectures.hpp"
42 #include "MachOFileAbstraction.hpp"
43 #include "CacheFileAbstraction.hpp"
58 const char* dependentsOfPath
;
59 const void* mappedCache
;
62 bool printDylibVersions
;
66 std::map
<uint32_t, const char*> pageToContent
;
67 uint64_t linkeditBase
;
68 bool dependentTargetFound
;
76 fprintf(stderr
, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map [ shared-cache-file ] | -slide_info | -info\n");
80 * Get the path to the native shared cache for this host
82 static const char* default_shared_cache_path() {
84 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"i386";
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";
90 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv6";
92 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv7";
94 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv7f";
96 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv7s";
98 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv7k";
100 #error unsupported architecture
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
);
110 * List dependencies from the mach-o header at headerAddr
111 * in the same format as 'otool -L'
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
;
119 if ( strcmp(options
.dependentsOfPath
, dylibInfo
->path
) != 0 )
121 if ( strcmp(segInfo
->name
, "__TEXT") != 0 )
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() ) {
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",
144 (compat_vers
>> 8) & 0xff,
145 (compat_vers
) & 0xff,
146 (current_vers
>> 16),
147 (current_vers
>> 8) & 0xff,
148 (current_vers
) & 0xff);
155 printf("\t%s\n", dylib_cmd
->name());
159 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
161 results
.dependentTargetFound
= true;
165 * Print out a dylib from the shared cache, optionally including the UUID or unslid load address
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
)
171 if ( strcmp(segInfo
->name
, "__TEXT") != 0 )
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]);
186 printf("< no uuid in dylib > ");
188 if ( dylibInfo
->isAlias
)
189 printf("[alias] %s\n", dylibInfo
->path
);
191 printf("%s\n", dylibInfo
->path
);
197 static void add_linkedit(uint32_t pageStart
, uint32_t pageEnd
, const char* message
, Results
& results
)
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
);
205 const char* oldMessage
= pos
->second
;
207 asprintf(&newMesssage
, "%s, %s", oldMessage
, message
);
208 results
.pageToContent
[p
] = newMesssage
;
209 ::free((void*)oldMessage
);
216 * get LINKEDIT info for dylib
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
)
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
;
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
);
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
);
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
);
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
);
265 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
271 * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built
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
);
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");
288 int main (int argc
, const char* argv
[]) {
290 const char* sharedCachePath
= default_shared_cache_path();
293 options
.mode
= modeNone
;
294 options
.printUUIDs
= false;
295 options
.printVMAddrs
= false;
296 options
.printDylibVersions
= false;
297 options
.dependentsOfPath
= NULL
;
299 for (uint32_t i
= 1; i
< argc
; i
++) {
300 const char* opt
= argv
[i
];
302 if (strcmp(opt
, "-list") == 0) {
303 checkMode(options
.mode
);
304 options
.mode
= modeList
;
306 else if (strcmp(opt
, "-dependents") == 0) {
307 checkMode(options
.mode
);
308 options
.mode
= modeDependencies
;
309 options
.dependentsOfPath
= argv
[++i
];
311 fprintf(stderr
, "Error: option -depdendents requires an argument\n");
316 else if (strcmp(opt
, "-linkedit") == 0) {
317 checkMode(options
.mode
);
318 options
.mode
= modeLinkEdit
;
320 else if (strcmp(opt
, "-info") == 0) {
321 checkMode(options
.mode
);
322 options
.mode
= modeInfo
;
324 else if (strcmp(opt
, "-slide_info") == 0) {
325 checkMode(options
.mode
);
326 options
.mode
= modeSlideInfo
;
328 else if (strcmp(opt
, "-map") == 0) {
329 checkMode(options
.mode
);
330 options
.mode
= modeMap
;
332 else if (strcmp(opt
, "-uuid") == 0) {
333 options
.printUUIDs
= true;
335 else if (strcmp(opt
, "-versions") == 0) {
336 options
.printDylibVersions
= true;
338 else if (strcmp(opt
, "-vmaddr") == 0) {
339 options
.printVMAddrs
= true;
342 fprintf(stderr
, "Error: unrecognized option %s\n", opt
);
348 sharedCachePath
= opt
;
352 if ( options
.mode
== modeNone
) {
353 fprintf(stderr
, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
358 if ( options
.mode
!= modeSlideInfo
) {
359 if ( options
.printUUIDs
&& (options
.mode
!= modeList
) )
360 fprintf(stderr
, "Warning: -uuid option ignored outside of -list mode\n");
362 if ( options
.printVMAddrs
&& (options
.mode
!= modeList
) )
363 fprintf(stderr
, "Warning: -vmaddr option ignored outside of -list mode\n");
365 if ( options
.printDylibVersions
&& (options
.mode
!= modeDependencies
) )
366 fprintf(stderr
, "Warning: -versions option ignored outside of -dependents mode\n");
368 if ( (options
.mode
== modeDependencies
) && (options
.dependentsOfPath
== NULL
) ) {
369 fprintf(stderr
, "Error: -dependents given, but no dylib path specified\n");
376 if ( ::stat(sharedCachePath
, &statbuf
) == -1 ) {
377 fprintf(stderr
, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath
, errno
);
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
);
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
);
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");
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
]);
414 else if ( options
.mode
== modeInfo
) {
415 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)options
.mappedCache
;
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]);
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));
441 segment_callback_t callback
;
442 if ( strcmp((char*)options
.mappedCache
, "dyld_v1 i386") == 0 ) {
443 switch ( options
.mode
) {
445 callback
= print_list
<x86
>;
448 callback
= print_map
<x86
>;
450 case modeDependencies
:
451 callback
= print_dependencies
<x86
>;
454 callback
= process_linkedit
<x86
>;
462 else if ( strcmp((char*)options
.mappedCache
, "dyld_v1 x86_64") == 0 ) {
463 switch ( options
.mode
) {
465 callback
= print_list
<x86_64
>;
468 callback
= print_map
<x86_64
>;
470 case modeDependencies
:
471 callback
= print_dependencies
<x86_64
>;
474 callback
= process_linkedit
<x86_64
>;
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
) {
486 callback
= print_list
<arm
>;
489 callback
= print_map
<arm
>;
491 case modeDependencies
:
492 callback
= print_dependencies
<arm
>;
495 callback
= process_linkedit
<arm
>;
504 fprintf(stderr
, "Error: unrecognized dyld shared cache magic.\n");
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
);
514 if ( iterateResult
!= 0 ) {
515 fprintf(stderr
, "Error: malformed shared cache file\n");
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
);
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
);