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
"arm64e";
142 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"arm64";
144 #error unsupported architecture
148 typedef void (*segment_callback_t
)(const dyld_shared_cache_dylib_info
* dylibInfo
, const dyld_shared_cache_segment_info
* segInfo
,
149 const Options
& options
, Results
& results
);
154 * List dependencies from the mach-o header at headerAddr
155 * in the same format as 'otool -L'
157 template <typename A
>
158 void print_dependencies(const dyld_shared_cache_dylib_info
* dylibInfo
, const dyld_shared_cache_segment_info
* segInfo
,
159 const Options
& options
, Results
& results
) {
160 typedef typename
A::P P
;
161 typedef typename
A::P::E E
;
163 if ( strcmp(options
.dependentsOfPath
, dylibInfo
->path
) != 0 )
165 if ( strcmp(segInfo
->name
, "__TEXT") != 0 )
168 const macho_dylib_command
<P
>* dylib_cmd
;
169 const macho_header
<P
>* mh
= (const macho_header
<P
>*)dylibInfo
->machHeader
;
170 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((uintptr_t)dylibInfo
->machHeader
+ sizeof(macho_header
<P
>));
171 const uint32_t cmd_count
= mh
->ncmds();
172 const macho_load_command
<P
>* cmd
= cmds
;
173 for (uint32_t i
= 0; i
< cmd_count
; ++i
) {
174 switch ( cmd
->cmd() ) {
177 case LC_REEXPORT_DYLIB
:
178 case LC_LOAD_WEAK_DYLIB
:
179 case LC_LOAD_UPWARD_DYLIB
:
180 dylib_cmd
= (macho_dylib_command
<P
>*)cmd
;
181 if ( options
.printDylibVersions
) {
182 uint32_t compat_vers
= dylib_cmd
->compatibility_version();
183 uint32_t current_vers
= dylib_cmd
->current_version();
184 printf("\t%s", dylib_cmd
->name());
185 if ( compat_vers
!= 0xFFFFFFFF ) {
186 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
188 (compat_vers
>> 8) & 0xff,
189 (compat_vers
) & 0xff,
190 (current_vers
>> 16),
191 (current_vers
>> 8) & 0xff,
192 (current_vers
) & 0xff);
199 printf("\t%s\n", dylib_cmd
->name());
203 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
205 results
.dependentTargetFound
= true;
209 * Print out a dylib from the shared cache, optionally including the UUID or unslid load address
211 template <typename A
>
212 void print_list(const dyld_shared_cache_dylib_info
* dylibInfo
, const dyld_shared_cache_segment_info
* segInfo
,
213 const Options
& options
, Results
& results
)
215 if ( strcmp(segInfo
->name
, "__TEXT") != 0 )
218 if ( options
.printVMAddrs
)
219 printf("0x%08llX ", segInfo
->address
);
220 if ( options
.printInodes
)
221 printf("0x%08llX 0x%08llX ", dylibInfo
->inode
, dylibInfo
->modTime
);
222 if ( options
.printUUIDs
) {
223 if ( dylibInfo
->uuid
!= NULL
) {
224 const uint8_t* uuid
= (uint8_t*)dylibInfo
->uuid
;;
225 printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ",
226 uuid
[0], uuid
[1], uuid
[2], uuid
[3],
227 uuid
[4], uuid
[5], uuid
[6], uuid
[7],
228 uuid
[8], uuid
[9], uuid
[10], uuid
[11],
229 uuid
[12], uuid
[13], uuid
[14], uuid
[15]);
232 printf("< no uuid in dylib > ");
234 if ( dylibInfo
->isAlias
)
235 printf("[alias] %s\n", dylibInfo
->path
);
237 printf("%s\n", dylibInfo
->path
);
241 template <typename A
>
242 void collect_size(const dyld_shared_cache_dylib_info
* dylibInfo
, const dyld_shared_cache_segment_info
* segInfo
,
243 const Options
& options
, Results
& results
)
245 if ( strcmp(segInfo
->name
, "__TEXT") != 0 )
247 if ( dylibInfo
->isAlias
)
251 info
.textSize
= segInfo
->fileSize
;
252 info
.path
= dylibInfo
->path
;
253 results
.textSegments
.push_back(info
);
259 static void add_linkedit(uint32_t pageStart
, uint32_t pageEnd
, const char* message
, Results
& results
)
261 for (uint32_t p
= pageStart
; p
<= pageEnd
; p
+= 4096) {
262 std::map
<uint32_t, const char*>::iterator pos
= results
.pageToContent
.find(p
);
263 if ( pos
== results
.pageToContent
.end() ) {
264 results
.pageToContent
[p
] = strdup(message
);
267 const char* oldMessage
= pos
->second
;
269 asprintf(&newMesssage
, "%s, %s", oldMessage
, message
);
270 results
.pageToContent
[p
] = newMesssage
;
271 ::free((void*)oldMessage
);
278 * get LINKEDIT info for dylib
280 template <typename A
>
281 void process_linkedit(const dyld_shared_cache_dylib_info
* dylibInfo
, const dyld_shared_cache_segment_info
* segInfo
,
282 const Options
& options
, Results
& results
) {
283 typedef typename
A::P P
;
284 typedef typename
A::P::E E
;
285 // filter out symlinks
286 if ( dylibInfo
->isAlias
)
288 const macho_header
<P
>* mh
= (const macho_header
<P
>*)dylibInfo
->machHeader
;
289 uint32_t ncmds
= mh
->ncmds();
290 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((long)mh
+ sizeof(macho_header
<P
>));
291 const macho_load_command
<P
>* cmd
= cmds
;
292 for (uint32_t i
= 0; i
< ncmds
; i
++) {
293 if ( cmd
->cmd() == LC_DYLD_INFO_ONLY
) {
294 macho_dyld_info_command
<P
>* dyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
296 const char* shortName
= strrchr(dylibInfo
->path
, '/') + 1;
297 // add export trie info
298 if ( dyldInfo
->export_size() != 0 ) {
299 //printf("export_off=0x%X\n", dyldInfo->export_off());
300 uint32_t exportPageOffsetStart
= dyldInfo
->export_off() & (-4096);
301 uint32_t exportPageOffsetEnd
= (dyldInfo
->export_off() + dyldInfo
->export_size()) & (-4096);
302 sprintf(message
, "exports from %s", shortName
);
303 add_linkedit(exportPageOffsetStart
, exportPageOffsetEnd
, message
, results
);
306 if ( dyldInfo
->bind_size() != 0 ) {
307 uint32_t bindPageOffsetStart
= dyldInfo
->bind_off() & (-4096);
308 uint32_t bindPageOffsetEnd
= (dyldInfo
->bind_off() + dyldInfo
->bind_size()) & (-4096);
309 sprintf(message
, "bindings from %s", shortName
);
310 add_linkedit(bindPageOffsetStart
, bindPageOffsetEnd
, message
, results
);
312 // add lazy binding info
313 if ( dyldInfo
->lazy_bind_size() != 0 ) {
314 uint32_t lazybindPageOffsetStart
= dyldInfo
->lazy_bind_off() & (-4096);
315 uint32_t lazybindPageOffsetEnd
= (dyldInfo
->lazy_bind_off() + dyldInfo
->lazy_bind_size()) & (-4096);
316 sprintf(message
, "lazy bindings from %s", shortName
);
317 add_linkedit(lazybindPageOffsetStart
, lazybindPageOffsetEnd
, message
, results
);
319 // add weak binding info
320 if ( dyldInfo
->weak_bind_size() != 0 ) {
321 uint32_t weakbindPageOffsetStart
= dyldInfo
->weak_bind_off() & (-4096);
322 uint32_t weakbindPageOffsetEnd
= (dyldInfo
->weak_bind_off() + dyldInfo
->weak_bind_size()) & (-4096);
323 sprintf(message
, "weak bindings from %s", shortName
);
324 add_linkedit(weakbindPageOffsetStart
, weakbindPageOffsetEnd
, message
, results
);
327 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
333 * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built
335 template <typename A
>
336 void print_map(const dyld_shared_cache_dylib_info
* dylibInfo
, const dyld_shared_cache_segment_info
* segInfo
, const Options
& options
, Results
& results
) {
337 if ( !dylibInfo
->isAlias
)
338 printf("0x%08llX - 0x%08llX %s %s\n", segInfo
->address
, segInfo
->address
+ segInfo
->fileSize
, segInfo
->name
, dylibInfo
->path
);
342 static void checkMode(Mode mode
) {
343 if ( mode
!= modeNone
) {
344 fprintf(stderr
, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, -extract, or -size\n");
350 int main (int argc
, const char* argv
[]) {
352 const char* sharedCachePath
= default_shared_cache_path();
355 options
.mode
= modeNone
;
356 options
.printUUIDs
= false;
357 options
.printVMAddrs
= false;
358 options
.printDylibVersions
= false;
359 options
.printInodes
= false;
360 options
.dependentsOfPath
= NULL
;
361 options
.extractionDir
= NULL
;
363 for (uint32_t i
= 1; i
< argc
; i
++) {
364 const char* opt
= argv
[i
];
366 if (strcmp(opt
, "-list") == 0) {
367 checkMode(options
.mode
);
368 options
.mode
= modeList
;
370 else if (strcmp(opt
, "-dependents") == 0) {
371 checkMode(options
.mode
);
372 options
.mode
= modeDependencies
;
373 options
.dependentsOfPath
= argv
[++i
];
375 fprintf(stderr
, "Error: option -depdendents requires an argument\n");
380 else if (strcmp(opt
, "-linkedit") == 0) {
381 checkMode(options
.mode
);
382 options
.mode
= modeLinkEdit
;
384 else if (strcmp(opt
, "-info") == 0) {
385 checkMode(options
.mode
);
386 options
.mode
= modeInfo
;
388 else if (strcmp(opt
, "-slide_info") == 0) {
389 checkMode(options
.mode
);
390 options
.mode
= modeSlideInfo
;
392 else if (strcmp(opt
, "-accelerator_info") == 0) {
393 checkMode(options
.mode
);
394 options
.mode
= modeAcceleratorInfo
;
396 else if (strcmp(opt
, "-text_info") == 0) {
397 checkMode(options
.mode
);
398 options
.mode
= modeTextInfo
;
400 else if (strcmp(opt
, "-local_symbols") == 0) {
401 checkMode(options
.mode
);
402 options
.mode
= modeLocalSymbols
;
404 else if (strcmp(opt
, "-map") == 0) {
405 checkMode(options
.mode
);
406 options
.mode
= modeMap
;
408 else if (strcmp(opt
, "-size") == 0) {
409 checkMode(options
.mode
);
410 options
.mode
= modeSize
;
412 else if (strcmp(opt
, "-extract") == 0) {
413 checkMode(options
.mode
);
414 options
.mode
= modeExtract
;
415 options
.extractionDir
= argv
[++i
];
417 fprintf(stderr
, "Error: option -extract requires a directory argument\n");
422 else if (strcmp(opt
, "-uuid") == 0) {
423 options
.printUUIDs
= true;
425 else if (strcmp(opt
, "-inode") == 0) {
426 options
.printInodes
= true;
428 else if (strcmp(opt
, "-versions") == 0) {
429 options
.printDylibVersions
= true;
431 else if (strcmp(opt
, "-vmaddr") == 0) {
432 options
.printVMAddrs
= true;
435 fprintf(stderr
, "Error: unrecognized option %s\n", opt
);
441 sharedCachePath
= opt
;
445 if ( options
.mode
== modeNone
) {
446 fprintf(stderr
, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
451 if ( options
.mode
!= modeSlideInfo
) {
452 if ( options
.printUUIDs
&& (options
.mode
!= modeList
) )
453 fprintf(stderr
, "Warning: -uuid option ignored outside of -list mode\n");
455 if ( options
.printVMAddrs
&& (options
.mode
!= modeList
) )
456 fprintf(stderr
, "Warning: -vmaddr option ignored outside of -list mode\n");
458 if ( options
.printDylibVersions
&& (options
.mode
!= modeDependencies
) )
459 fprintf(stderr
, "Warning: -versions option ignored outside of -dependents mode\n");
461 if ( (options
.mode
== modeDependencies
) && (options
.dependentsOfPath
== NULL
) ) {
462 fprintf(stderr
, "Error: -dependents given, but no dylib path specified\n");
469 if ( ::stat(sharedCachePath
, &statbuf
) == -1 ) {
470 fprintf(stderr
, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath
, errno
);
474 int cache_fd
= ::open(sharedCachePath
, O_RDONLY
);
475 if ( cache_fd
< 0 ) {
476 fprintf(stderr
, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath
, errno
);
479 options
.mappedCache
= ::mmap(NULL
, (size_t)statbuf
.st_size
, PROT_READ
, MAP_PRIVATE
, cache_fd
, 0);
480 if (options
.mappedCache
== MAP_FAILED
) {
481 fprintf(stderr
, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath
, errno
);
485 if ( options
.mode
== modeSlideInfo
) {
486 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)options
.mappedCache
;
487 if ( header
->slideInfoOffset() == 0 ) {
488 fprintf(stderr
, "Error: dyld shared cache does not contain slide info\n");
491 const dyldCacheFileMapping
<LittleEndian
>* mappings
= (dyldCacheFileMapping
<LittleEndian
>*)((char*)options
.mappedCache
+ header
->mappingOffset());
492 const dyldCacheFileMapping
<LittleEndian
>* dataMapping
= &mappings
[1];
493 uint64_t dataStartAddress
= dataMapping
->address();
494 uint64_t dataSize
= dataMapping
->size();
495 const dyldCacheSlideInfo
<LittleEndian
>* slideInfoHeader
= (dyldCacheSlideInfo
<LittleEndian
>*)((char*)options
.mappedCache
+header
->slideInfoOffset());
496 printf("slide info version=%d\n", slideInfoHeader
->version());
497 if ( slideInfoHeader
->version() == 1 ) {
498 printf("toc_count=%d, data page count=%lld\n", slideInfoHeader
->toc_count(), dataSize
/4096);
499 const dyldCacheSlideInfoEntry
* entries
= (dyldCacheSlideInfoEntry
*)((char*)slideInfoHeader
+ slideInfoHeader
->entries_offset());
500 for(int i
=0; i
< slideInfoHeader
->toc_count(); ++i
) {
501 printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress
+ i
*4096, i
, slideInfoHeader
->toc(i
));
502 const dyldCacheSlideInfoEntry
* entry
= &entries
[slideInfoHeader
->toc(i
)];
503 for(int j
=0; j
< slideInfoHeader
->entries_size(); ++j
)
504 printf("%02X", entry
->bits
[j
]);
508 else if ( slideInfoHeader
->version() == 2 ) {
509 const dyldCacheSlideInfo2
<LittleEndian
>* slideInfo
= (dyldCacheSlideInfo2
<LittleEndian
>*)(slideInfoHeader
);
510 printf("page_size=%d\n", slideInfo
->page_size());
511 printf("delta_mask=0x%016llX\n", slideInfo
->delta_mask());
512 printf("value_add=0x%016llX\n", slideInfo
->value_add());
513 printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo
->page_starts_count(), slideInfo
->page_extras_count());
514 const uint16_t* starts
= (uint16_t* )((char*)slideInfo
+ slideInfo
->page_starts_offset());
515 const uint16_t* extras
= (uint16_t* )((char*)slideInfo
+ slideInfo
->page_extras_offset());
516 for (int i
=0; i
< slideInfo
->page_starts_count(); ++i
) {
517 const uint16_t start
= starts
[i
];
518 if ( start
== DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE
) {
519 printf("page[% 5d]: no rebasing\n", i
);
521 else if ( start
& DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
) {
522 printf("page[% 5d]: ", i
);
523 int j
=(start
& 0x3FFF);
526 uint16_t aStart
= extras
[j
];
527 printf("start=0x%04X ", aStart
& 0x3FFF);
528 done
= (extras
[j
] & DYLD_CACHE_SLIDE_PAGE_ATTR_END
);
534 printf("page[% 5d]: start=0x%04X\n", i
, starts
[i
]);
539 else if ( options
.mode
== modeInfo
) {
540 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)options
.mappedCache
;
542 if ( header
->mappingOffset() >= 0x68 ) {
543 const uint8_t* uuid
= header
->uuid();
544 printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
545 uuid
[0], uuid
[1], uuid
[2], uuid
[3],
546 uuid
[4], uuid
[5], uuid
[6], uuid
[7],
547 uuid
[8], uuid
[9], uuid
[10], uuid
[11],
548 uuid
[12], uuid
[13], uuid
[14], uuid
[15]);
553 if ( header
->mappingOffset() >= 0xE0 ) {
554 // HACK until this uses new header
555 uint32_t platform
= *((uint32_t*)(((char*)header
) + 0xD8));
556 uint32_t simulator
= *((uint32_t*)(((char*)header
) + 0xDC));
559 printf("platform: macOS\n");
562 if ( simulator
& 0x400 )
563 printf("platform: iOS simulator\n");
565 printf("platform: iOS\n");
568 if ( simulator
& 0x400 )
569 printf("platform: tvOS simulator\n");
571 printf("platform: tvOS\n");
574 if ( simulator
& 0x400 )
575 printf("platform: watchOS simulator\n");
577 printf("platform: watchOS\n");
580 printf("platform: bridgeOS\n");
583 printf("platform: 0x%08X 0x%08X\n", platform
, simulator
);
586 printf("image count: %u\n", header
->imagesCount());
587 if ( (header
->mappingOffset() >= 0x78) && (header
->branchPoolsOffset() != 0) ) {
588 printf("branch pool count: %u\n", header
->branchPoolsCount());
590 printf("mappings:\n");
591 const dyldCacheFileMapping
<LittleEndian
>* mappings
= (dyldCacheFileMapping
<LittleEndian
>*)((char*)options
.mappedCache
+ header
->mappingOffset());
592 for (uint32_t i
=0; i
< header
->mappingCount(); ++i
) {
593 if ( mappings
[i
].init_prot() & VM_PROT_EXECUTE
)
594 printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
595 mappings
[i
].size()/(1024*1024), mappings
[i
].file_offset(), mappings
[i
].file_offset() + mappings
[i
].size(),
596 mappings
[i
].address(), mappings
[i
].address() + mappings
[i
].size());
597 else if ( mappings
[i
]. init_prot() & VM_PROT_WRITE
)
598 printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
599 mappings
[i
].size()/(1024*1024), mappings
[i
].file_offset(), mappings
[i
].file_offset() + mappings
[i
].size(),
600 mappings
[i
].address(), mappings
[i
].address() + mappings
[i
].size());
601 else if ( mappings
[i
].init_prot() & VM_PROT_READ
)
602 printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
603 mappings
[i
].size()/(1024*1024), mappings
[i
].file_offset(), mappings
[i
].file_offset() + mappings
[i
].size(),
604 mappings
[i
].address(), mappings
[i
].address() + mappings
[i
].size());
606 if ( header
->codeSignatureOffset() != 0 ) {
607 uint64_t size
= statbuf
.st_size
- header
->codeSignatureOffset();
608 uint64_t csAddr
= mappings
[header
->mappingCount()-1].address() + mappings
[header
->mappingCount()-1].size();
610 printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n",
611 size
/(1024*1024), header
->codeSignatureOffset(), header
->codeSignatureOffset() + size
, csAddr
, csAddr
+ size
);
613 printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n",
614 header
->slideInfoSize()/1024, header
->slideInfoOffset(), header
->slideInfoOffset() + header
->slideInfoSize());
615 if ( header
->localSymbolsOffset() != 0 )
616 printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n",
617 header
->localSymbolsSize()/(1024*1024), header
->localSymbolsOffset(), header
->localSymbolsOffset() + header
->localSymbolsSize());
618 if ( (header
->mappingOffset() >= 0x78) && (header
->accelerateInfoSize() != 0) )
619 printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n",
620 header
->accelerateInfoSize()/1024, header
->accelerateInfoAddr(), header
->accelerateInfoAddr() + header
->accelerateInfoSize());
622 else if ( options
.mode
== modeAcceleratorInfo
) {
623 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)options
.mappedCache
;
624 if ( (header
->mappingOffset() < sizeof(dyldCacheHeader
<LittleEndian
>)) || (header
->accelerateInfoSize() == 0) ) {
625 printf("no accelerator info\n");
628 const dyldCacheFileMapping
<LittleEndian
>* mappings
= (dyldCacheFileMapping
<LittleEndian
>*)((char*)options
.mappedCache
+ header
->mappingOffset());
629 uint64_t aiAddr
= header
->accelerateInfoAddr();
630 dyldCacheAcceleratorInfo
<LittleEndian
>* accelInfo
= NULL
;
631 for (uint32_t i
=0; i
< header
->mappingCount(); ++i
) {
632 if ( (mappings
[i
].address() <= aiAddr
) && (aiAddr
< mappings
[i
].address()+mappings
[i
].size()) ) {
633 uint64_t offset
= aiAddr
- mappings
[i
].address() + mappings
[i
].file_offset();
634 accelInfo
= (dyldCacheAcceleratorInfo
<LittleEndian
>*)((uint8_t*)options
.mappedCache
+ offset
);
637 if ( accelInfo
== NULL
) {
638 printf("accelerator info not in any mapped range\n");
641 const dyldCacheImageInfo
<LittleEndian
>* images
= (dyldCacheImageInfo
<LittleEndian
>*)((char*)options
.mappedCache
+ header
->imagesOffset());
642 const dyldCacheImageInfoExtra
<LittleEndian
>* imagesExtra
= (dyldCacheImageInfoExtra
<LittleEndian
>*)((char*)accelInfo
+ accelInfo
->imagesExtrasOffset());
643 const uint16_t* dependencyArray
= (uint16_t*)((char*)accelInfo
+ accelInfo
->depListOffset());
644 const uint16_t* reExportArray
= (uint16_t*)((char*)accelInfo
+ accelInfo
->reExportListOffset());
645 printf("extra image info (count=%u):\n", accelInfo
->imageExtrasCount());
646 for (uint32_t i
=0; i
< accelInfo
->imageExtrasCount(); ++i
) {
647 printf(" image[%3u] %s:\n", i
, (char*)options
.mappedCache
+images
[i
].pathFileOffset());
648 printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra
[i
].exportsTrieAddr(), imagesExtra
[i
].exportsTrieSize());
649 if ( imagesExtra
[i
].weakBindingsSize() )
650 printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra
[i
].weakBindingsAddr(), imagesExtra
[i
].weakBindingsSize());
651 printf(" dependents: ");
652 for (uint32_t d
=imagesExtra
[i
].dependentsStartArrayIndex(); dependencyArray
[d
] != 0xFFFF; ++d
) {
653 uint16_t depIndex
= dependencyArray
[d
];
654 if ( depIndex
& 0x8000 )
655 printf(" up(%d) ", depIndex
& 0x7FFF);
657 printf(" %d ", depIndex
);
660 printf(" re-exports: ");
661 for (uint32_t r
=imagesExtra
[i
].reExportsStartArrayIndex(); reExportArray
[r
] != 0xFFFF; ++r
)
662 printf(" %d ", reExportArray
[r
]);
665 printf("libdyld.dylib:\n");
666 printf(" __dyld section address: 0x%llX\n", accelInfo
->dyldSectionAddr());
667 printf("initializers (count=%u):\n", accelInfo
->initializersCount());
668 const dyldCacheAcceleratorInitializer
<LittleEndian
>* initializers
= (dyldCacheAcceleratorInitializer
<LittleEndian
>*)((char*)accelInfo
+ accelInfo
->initializersOffset());
669 for (uint32_t i
=0; i
< accelInfo
->initializersCount(); ++i
) {
670 printf(" image[%3u] 0x%llX\n", initializers
[i
].imageIndex(), mappings
[0].address() + initializers
[i
].functionOffset());
672 printf("DOF sections (count=%u):\n", accelInfo
->dofSectionsCount());
673 const dyldCacheAcceleratorDOFEntry
<LittleEndian
>* dofs
= (dyldCacheAcceleratorDOFEntry
<LittleEndian
>*)((char*)accelInfo
+ accelInfo
->dofSectionsOffset());
674 for (uint32_t i
=0; i
< accelInfo
->dofSectionsCount(); ++i
) {
675 printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs
[i
].imageIndex(), dofs
[i
].sectionAddress(), dofs
[i
].sectionAddress()+dofs
[i
].sectionSize());
677 printf("bottom up order (count=%u):\n", accelInfo
->imageExtrasCount());
678 const uint16_t* bottomUpArray
= (uint16_t*)((char*)accelInfo
+ accelInfo
->bottomUpListOffset());
679 for (uint32_t i
=0; i
< accelInfo
->imageExtrasCount(); ++i
) {
680 unsigned imageIndex
= bottomUpArray
[i
];
681 if ( imageIndex
< accelInfo
->imageExtrasCount() )
682 printf(" image[%3u] %s\n", imageIndex
, (char*)options
.mappedCache
+ images
[imageIndex
].pathFileOffset());
684 printf(" image[%3u] BAD INDEX\n", imageIndex
);
686 printf("range table (count=%u):\n", accelInfo
->rangeTableCount());
687 const dyldCacheAcceleratorRangeEntry
<LittleEndian
>* rangeTable
= (dyldCacheAcceleratorRangeEntry
<LittleEndian
>*)((char*)accelInfo
+ accelInfo
->rangeTableOffset());
688 for (uint32_t i
=0; i
< accelInfo
->rangeTableCount(); ++i
) {
689 const dyldCacheAcceleratorRangeEntry
<LittleEndian
>& entry
= rangeTable
[i
];
690 printf(" 0x%llX -> 0x%llX %s\n", entry
.startAddress(), entry
.startAddress() + entry
.size(), (char*)options
.mappedCache
+ images
[entry
.imageIndex()].pathFileOffset());
692 printf("dylib trie (size=%u):\n", accelInfo
->dylibTrieSize());
693 const uint8_t* dylibTrieStart
= (uint8_t*)accelInfo
+ accelInfo
->dylibTrieOffset();
694 const uint8_t* dylibTrieEnd
= dylibTrieStart
+ accelInfo
->dylibTrieSize();
695 std::vector
<DylibIndexTrie::Entry
> dylibEntries
;
696 if ( !Trie
<DylibIndex
>::parseTrie(dylibTrieStart
, dylibTrieEnd
, dylibEntries
) )
697 printf(" malformed dylibs trie\n");
698 for (const DylibIndexTrie::Entry
& x
: dylibEntries
) {
699 printf(" image[%3u] %s\n", x
.info
.index
, x
.name
.c_str());
704 else if ( options
.mode
== modeTextInfo
) {
705 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)options
.mappedCache
;
706 if ( (header
->mappingOffset() < sizeof(dyldCacheHeader
<LittleEndian
>)) || (header
->imagesTextCount() == 0) ) {
707 printf("no text info\n");
710 const dyldCacheImageTextInfo
<LittleEndian
>* imagesText
= (dyldCacheImageTextInfo
<LittleEndian
>*)((char*)options
.mappedCache
+ header
->imagesTextOffset());
711 const dyldCacheImageTextInfo
<LittleEndian
>* imagesTextEnd
= &imagesText
[header
->imagesTextCount()];
712 printf("dylib text infos (count=%llu):\n", header
->imagesTextCount());
713 for (const dyldCacheImageTextInfo
<LittleEndian
>* p
=imagesText
; p
< imagesTextEnd
; ++p
) {
714 printf(" 0x%09llX -> 0x%09llX <", p
->loadAddress(), p
->loadAddress() + p
->textSegmentSize());
715 for (int i
=0; i
<16; ++i
) {
724 printf("%02X", p
->uuid()[i
]);
726 printf("> %s\n", (char*)options
.mappedCache
+ p
->pathOffset());
730 else if ( options
.mode
== modeLocalSymbols
) {
731 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)options
.mappedCache
;
732 if ( header
->localSymbolsOffset() == 0 ) {
733 fprintf(stderr
, "Error: dyld shared cache does not contain local symbols info\n");
736 const bool is64
= (strstr((char*)options
.mappedCache
, "64") != NULL
);
737 const dyldCacheImageInfo
<LittleEndian
>* imageInfos
= (dyldCacheImageInfo
<LittleEndian
>*)((char*)options
.mappedCache
+ header
->imagesOffset());
738 const dyldCacheLocalSymbolsInfo
<LittleEndian
>* localsInfo
= (dyldCacheLocalSymbolsInfo
<LittleEndian
>*)((char*)options
.mappedCache
+ header
->localSymbolsOffset());
739 const uint32_t nlistFileOffset
= (uint32_t)(header
->localSymbolsOffset() + localsInfo
->nlistOffset());
740 const uint32_t nlistCount
= localsInfo
->nlistCount();
741 const uint32_t nlistByteSize
= is64
? nlistCount
*16 : nlistCount
*12;
742 const uint32_t stringsFileOffset
= (uint32_t)(header
->localSymbolsOffset() + localsInfo
->stringsOffset());
743 const uint32_t stringsSize
= localsInfo
->stringsSize();
744 const uint32_t entriesCount
= localsInfo
->entriesCount();
745 const dyldCacheLocalSymbolEntry
<LittleEndian
>* entries
= (dyldCacheLocalSymbolEntry
<LittleEndian
>*)((char*)localsInfo
+ localsInfo
->entriesOffset());
746 printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize
/(1024*1024), nlistFileOffset
, nlistFileOffset
+nlistByteSize
);
747 printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize
/(1024*1024), stringsFileOffset
, stringsFileOffset
+stringsSize
);
748 printf("local symbols by dylib (count=%d):\n", entriesCount
);
749 //const char* stringPool = (char*)options.mappedCache + stringsFileOffset;
750 for (int i
=0; i
< entriesCount
; ++i
) {
751 const char* imageName
= (char*)options
.mappedCache
+ imageInfos
[i
].pathFileOffset();
752 printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries
[i
].nlistStartIndex(), entries
[i
].nlistCount(), imageName
);
755 const nlist_64
* symTab
= (nlist_64
*)((char*)options
.mappedCache
+ nlistFileOffset
);
756 for (int e
=0; e
< entries
[i
].nlistCount(); ++e
) {
757 const nlist_64
* entry
= &symTab
[entries
[i
].nlistStartIndex()+e
];
758 printf(" nlist[%d].str=%d, %s\n", e
, entry
->n_un
.n_strx
, &stringPool
[entry
->n_un
.n_strx
]);
759 printf(" nlist[%d].value=0x%0llX\n", e
, entry
->n_value
);
765 else if ( options
.mode
== modeExtract
) {
766 char pathBuffer
[PATH_MAX
];
767 uint32_t bufferSize
= PATH_MAX
;
768 if ( _NSGetExecutablePath(pathBuffer
, &bufferSize
) != 0 ) {
769 fprintf(stderr
, "Error: could not get path of program\n");
772 char* last
= strrchr(pathBuffer
, '/');
773 strcpy(last
+1, "../../lib/dsc_extractor.bundle");
774 void* handle
= dlopen(pathBuffer
, RTLD_LAZY
);
775 if ( handle
== NULL
) {
776 fprintf(stderr
, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer
);
780 typedef int (*extractor_proc
)(const char* shared_cache_file_path
, const char* extraction_root_path
,
781 void (^progress
)(unsigned current
, unsigned total
));
783 extractor_proc proc
= (extractor_proc
)dlsym(handle
, "dyld_shared_cache_extract_dylibs_progress");
784 if ( proc
== NULL
) {
785 fprintf(stderr
, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
789 int result
= (*proc
)(sharedCachePath
, options
.extractionDir
, ^(unsigned c
, unsigned total
) { } );
793 segment_callback_t callback
= nullptr;
794 if ( strcmp((char*)options
.mappedCache
, "dyld_v1 i386") == 0 ) {
795 switch ( options
.mode
) {
797 callback
= print_list
<x86
>;
800 callback
= print_map
<x86
>;
802 case modeDependencies
:
803 callback
= print_dependencies
<x86
>;
806 callback
= process_linkedit
<x86
>;
809 callback
= collect_size
<x86
>;
814 case modeAcceleratorInfo
:
816 case modeLocalSymbols
:
821 else if ( (strcmp((char*)options
.mappedCache
, "dyld_v1 x86_64") == 0)
822 || (strcmp((char*)options
.mappedCache
, "dyld_v1 x86_64h") == 0) ) {
823 switch ( options
.mode
) {
825 callback
= print_list
<x86_64
>;
828 callback
= print_map
<x86_64
>;
830 case modeDependencies
:
831 callback
= print_dependencies
<x86_64
>;
834 callback
= process_linkedit
<x86_64
>;
837 callback
= collect_size
<x86_64
>;
842 case modeAcceleratorInfo
:
844 case modeLocalSymbols
:
849 else if ( (strncmp((char*)options
.mappedCache
, "dyld_v1 armv", 14) == 0)
850 || (strncmp((char*)options
.mappedCache
, "dyld_v1 armv", 13) == 0) ) {
851 switch ( options
.mode
) {
853 callback
= print_list
<arm
>;
856 callback
= print_map
<arm
>;
858 case modeDependencies
:
859 callback
= print_dependencies
<arm
>;
862 callback
= process_linkedit
<arm
>;
865 callback
= collect_size
<arm
>;
870 case modeAcceleratorInfo
:
872 case modeLocalSymbols
:
877 else if ( (strcmp((char*)options
.mappedCache
, "dyld_v1 arm64") == 0)
878 || (strcmp((char*)options
.mappedCache
, "dyld_v1 arm64e") == 0) ) {
879 switch ( options
.mode
) {
881 callback
= print_list
<arm64
>;
884 callback
= print_map
<arm64
>;
886 case modeDependencies
:
887 callback
= print_dependencies
<arm64
>;
890 callback
= process_linkedit
<arm64
>;
893 callback
= collect_size
<arm64
>;
898 case modeAcceleratorInfo
:
900 case modeLocalSymbols
:
906 fprintf(stderr
, "Error: unrecognized dyld shared cache magic.\n");
910 __block Results results
;
911 results
.dependentTargetFound
= false;
912 int iterateResult
= dyld_shared_cache_iterate(options
.mappedCache
, (uint32_t)statbuf
.st_size
,
913 ^(const dyld_shared_cache_dylib_info
* dylibInfo
, const dyld_shared_cache_segment_info
* segInfo
) {
914 (callback
)(dylibInfo
, segInfo
, options
, results
);
916 if ( iterateResult
!= 0 ) {
917 fprintf(stderr
, "Error: malformed shared cache file\n");
921 if ( options
.mode
== modeLinkEdit
) {
922 // dump -linkedit information
923 for (std::map
<uint32_t, const char*>::iterator it
= results
.pageToContent
.begin(); it
!= results
.pageToContent
.end(); ++it
) {
924 printf("0x%08X %s\n", it
->first
, it
->second
);
927 else if ( options
.mode
== modeSize
) {
928 std::sort(results
.textSegments
.begin(), results
.textSegments
.end(), TextInfoSorter());
929 for (std::vector
<TextInfo
>::iterator it
= results
.textSegments
.begin(); it
!= results
.textSegments
.end(); ++it
) {
930 printf(" 0x%08llX %s\n", it
->textSize
, it
->path
);
934 if ( (options
.mode
== modeDependencies
) && options
.dependentsOfPath
&& !results
.dependentTargetFound
) {
935 fprintf(stderr
, "Error: could not find '%s' in the shared cache at\n %s\n", options
.dependentsOfPath
, sharedCachePath
);