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@
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>
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"
68 const char* dependentsOfPath
;
69 const void* mappedCache
;
70 const char* extractionDir
;
73 bool printDylibVersions
;
82 struct TextInfoSorter
{
83 bool operator()(const TextInfo
& left
, const TextInfo
& right
) {
84 return (left
.textSize
> right
.textSize
);
89 std::map
<uint32_t, const char*> pageToContent
;
90 uint64_t linkeditBase
;
91 bool dependentTargetFound
;
92 std::vector
<TextInfo
> textSegments
;
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");
102 static bool isHaswell()
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
)
112 return ( info
.cpu_subtype
== CPU_SUBTYPE_X86_64_H
);
117 * Get the path to the native shared cache for this host
119 static const char* default_shared_cache_path() {
121 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"i386";
124 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"x86_64h";
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";
140 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"arm64";
142 #error unsupported architecture
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
);
152 * List dependencies from the mach-o header at headerAddr
153 * in the same format as 'otool -L'
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
;
161 if ( strcmp(options
.dependentsOfPath
, dylibInfo
->path
) != 0 )
163 if ( strcmp(segInfo
->name
, "__TEXT") != 0 )
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() ) {
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",
186 (compat_vers
>> 8) & 0xff,
187 (compat_vers
) & 0xff,
188 (current_vers
>> 16),
189 (current_vers
>> 8) & 0xff,
190 (current_vers
) & 0xff);
197 printf("\t%s\n", dylib_cmd
->name());
201 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
203 results
.dependentTargetFound
= true;
207 * Print out a dylib from the shared cache, optionally including the UUID or unslid load address
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
)
213 if ( strcmp(segInfo
->name
, "__TEXT") != 0 )
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]);
230 printf("< no uuid in dylib > ");
232 if ( dylibInfo
->isAlias
)
233 printf("[alias] %s\n", dylibInfo
->path
);
235 printf("%s\n", dylibInfo
->path
);
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
)
243 if ( strcmp(segInfo
->name
, "__TEXT") != 0 )
245 if ( dylibInfo
->isAlias
)
249 info
.textSize
= segInfo
->fileSize
;
250 info
.path
= dylibInfo
->path
;
251 results
.textSegments
.push_back(info
);
252 size_t size
= segInfo
->fileSize
;
258 static void add_linkedit(uint32_t pageStart
, uint32_t pageEnd
, const char* message
, Results
& results
)
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
);
266 const char* oldMessage
= pos
->second
;
268 asprintf(&newMesssage
, "%s, %s", oldMessage
, message
);
269 results
.pageToContent
[p
] = newMesssage
;
270 ::free((void*)oldMessage
);
277 * get LINKEDIT info for dylib
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
)
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
;
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
);
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
);
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
);
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
);
326 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
332 * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built
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
);
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");
349 int main (int argc
, const char* argv
[]) {
351 const char* sharedCachePath
= default_shared_cache_path();
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
;
362 for (uint32_t i
= 1; i
< argc
; i
++) {
363 const char* opt
= argv
[i
];
365 if (strcmp(opt
, "-list") == 0) {
366 checkMode(options
.mode
);
367 options
.mode
= modeList
;
369 else if (strcmp(opt
, "-dependents") == 0) {
370 checkMode(options
.mode
);
371 options
.mode
= modeDependencies
;
372 options
.dependentsOfPath
= argv
[++i
];
374 fprintf(stderr
, "Error: option -depdendents requires an argument\n");
379 else if (strcmp(opt
, "-linkedit") == 0) {
380 checkMode(options
.mode
);
381 options
.mode
= modeLinkEdit
;
383 else if (strcmp(opt
, "-info") == 0) {
384 checkMode(options
.mode
);
385 options
.mode
= modeInfo
;
387 else if (strcmp(opt
, "-slide_info") == 0) {
388 checkMode(options
.mode
);
389 options
.mode
= modeSlideInfo
;
391 else if (strcmp(opt
, "-accelerator_info") == 0) {
392 checkMode(options
.mode
);
393 options
.mode
= modeAcceleratorInfo
;
395 else if (strcmp(opt
, "-text_info") == 0) {
396 checkMode(options
.mode
);
397 options
.mode
= modeTextInfo
;
399 else if (strcmp(opt
, "-local_symbols") == 0) {
400 checkMode(options
.mode
);
401 options
.mode
= modeLocalSymbols
;
403 else if (strcmp(opt
, "-map") == 0) {
404 checkMode(options
.mode
);
405 options
.mode
= modeMap
;
407 else if (strcmp(opt
, "-size") == 0) {
408 checkMode(options
.mode
);
409 options
.mode
= modeSize
;
411 else if (strcmp(opt
, "-extract") == 0) {
412 checkMode(options
.mode
);
413 options
.mode
= modeExtract
;
414 options
.extractionDir
= argv
[++i
];
416 fprintf(stderr
, "Error: option -extract requires a directory argument\n");
421 else if (strcmp(opt
, "-uuid") == 0) {
422 options
.printUUIDs
= true;
424 else if (strcmp(opt
, "-inode") == 0) {
425 options
.printInodes
= true;
427 else if (strcmp(opt
, "-versions") == 0) {
428 options
.printDylibVersions
= true;
430 else if (strcmp(opt
, "-vmaddr") == 0) {
431 options
.printVMAddrs
= true;
434 fprintf(stderr
, "Error: unrecognized option %s\n", opt
);
440 sharedCachePath
= opt
;
444 if ( options
.mode
== modeNone
) {
445 fprintf(stderr
, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
450 if ( options
.mode
!= modeSlideInfo
) {
451 if ( options
.printUUIDs
&& (options
.mode
!= modeList
) )
452 fprintf(stderr
, "Warning: -uuid option ignored outside of -list mode\n");
454 if ( options
.printVMAddrs
&& (options
.mode
!= modeList
) )
455 fprintf(stderr
, "Warning: -vmaddr option ignored outside of -list mode\n");
457 if ( options
.printDylibVersions
&& (options
.mode
!= modeDependencies
) )
458 fprintf(stderr
, "Warning: -versions option ignored outside of -dependents mode\n");
460 if ( (options
.mode
== modeDependencies
) && (options
.dependentsOfPath
== NULL
) ) {
461 fprintf(stderr
, "Error: -dependents given, but no dylib path specified\n");
468 if ( ::stat(sharedCachePath
, &statbuf
) == -1 ) {
469 fprintf(stderr
, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath
, errno
);
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
);
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
);
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");
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
]);
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
);
520 else if ( start
& DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
) {
521 printf("page[% 5d]: ", i
);
522 int j
=(start
& 0x3FFF);
525 uint16_t aStart
= extras
[j
];
526 printf("start=0x%04X ", aStart
& 0x3FFF);
527 done
= (extras
[j
] & DYLD_CACHE_SLIDE_PAGE_ATTR_END
);
533 printf("page[% 5d]: start=0x%04X\n", i
, starts
[i
]);
538 else if ( options
.mode
== modeInfo
) {
539 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)options
.mappedCache
;
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]);
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());
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());
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();
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
);
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());
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");
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
);
603 if ( accelInfo
== NULL
) {
604 printf("accelerator info not in any mapped range\n");
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);
623 printf(" %d ", depIndex
);
626 printf(" re-exports: ");
627 for (uint32_t r
=imagesExtra
[i
].reExportsStartArrayIndex(); reExportArray
[r
] != 0xFFFF; ++r
)
628 printf(" %d ", reExportArray
[r
]);
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());
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());
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());
650 printf(" image[%3u] BAD INDEX\n", imageIndex
);
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());
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());
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");
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
) {
690 printf("%02X", p
->uuid()[i
]);
692 printf("> %s\n", (char*)options
.mappedCache
+ p
->pathOffset());
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");
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
);
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
);
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");
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
);
746 typedef int (*extractor_proc
)(const char* shared_cache_file_path
, const char* extraction_root_path
,
747 void (^progress
)(unsigned current
, unsigned total
));
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");
755 int result
= (*proc
)(sharedCachePath
, options
.extractionDir
, ^(unsigned c
, unsigned total
) { } );
759 segment_callback_t callback
;
760 if ( strcmp((char*)options
.mappedCache
, "dyld_v1 i386") == 0 ) {
761 switch ( options
.mode
) {
763 callback
= print_list
<x86
>;
766 callback
= print_map
<x86
>;
768 case modeDependencies
:
769 callback
= print_dependencies
<x86
>;
772 callback
= process_linkedit
<x86
>;
775 callback
= collect_size
<x86
>;
780 case modeAcceleratorInfo
:
782 case modeLocalSymbols
:
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
) {
791 callback
= print_list
<x86_64
>;
794 callback
= print_map
<x86_64
>;
796 case modeDependencies
:
797 callback
= print_dependencies
<x86_64
>;
800 callback
= process_linkedit
<x86_64
>;
803 callback
= collect_size
<x86_64
>;
808 case modeAcceleratorInfo
:
810 case modeLocalSymbols
:
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
) {
819 callback
= print_list
<arm
>;
822 callback
= print_map
<arm
>;
824 case modeDependencies
:
825 callback
= print_dependencies
<arm
>;
828 callback
= process_linkedit
<arm
>;
831 callback
= collect_size
<arm
>;
836 case modeAcceleratorInfo
:
838 case modeLocalSymbols
:
843 else if ( strcmp((char*)options
.mappedCache
, "dyld_v1 arm64") == 0 ) {
844 switch ( options
.mode
) {
846 callback
= print_list
<arm64
>;
849 callback
= print_map
<arm64
>;
851 case modeDependencies
:
852 callback
= print_dependencies
<arm64
>;
855 callback
= process_linkedit
<arm64
>;
858 callback
= collect_size
<arm64
>;
863 case modeAcceleratorInfo
:
865 case modeLocalSymbols
:
871 fprintf(stderr
, "Error: unrecognized dyld shared cache magic.\n");
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
);
881 if ( iterateResult
!= 0 ) {
882 fprintf(stderr
, "Error: malformed shared cache file\n");
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
);
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
);
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
);