1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2009-2010 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>
41 #include "dsc_iterator.h"
42 #include "dyld_cache_format.h"
43 #include "Architectures.hpp"
44 #include "MachOFileAbstraction.hpp"
45 #include "CacheFileAbstraction.hpp"
49 #define OP_LIST_DEPENDENCIES 1
50 #define OP_LIST_DYLIBS 2
51 #define OP_LIST_LINKEDIT 3
55 // Define this here so we can work with or without block support
56 typedef void (*segment_callback_t
)(const char* dylib
, const char* segName
, uint64_t offset
, uint64_t sizem
,
57 uint64_t mappedddress
, uint64_t slide
, void* userData
);
59 struct seg_callback_args
{
61 uint32_t target_found
;
65 uint8_t print_vmaddrs
;
66 uint8_t print_dylib_versions
;
70 fprintf(stderr
, "Usage: dscutil -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit [ shared-cache-file ]\n");
74 * Get the path to the native shared cache for this host
76 static const char* default_shared_cache_path() {
78 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"i386";
80 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"x86_64";
81 #elif __ARM_ARCH_5TEJ__
82 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv5";
84 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv6";
86 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv7";
88 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv7f";
90 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv7k";
92 #error unsupported architecture
97 * Get a vector of all the load commands from the header pointed to by headerAddr
100 std::vector
<macho_load_command
<typename
A::P
>* > get_load_cmds(void *headerAddr
) {
101 typedef typename
A::P P
;
102 typedef typename
A::P::E E
;
104 std::vector
<macho_load_command
<P
>* > cmd_vector
;
106 const macho_header
<P
>* mh
= (const macho_header
<P
>*)headerAddr
;
107 uint32_t ncmds
= mh
->ncmds();
108 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((long)mh
+ sizeof(macho_header
<P
>));
109 const macho_load_command
<P
>* cmd
= cmds
;
111 for (uint32_t i
= 0; i
< ncmds
; i
++) {
112 cmd_vector
.push_back((macho_load_command
<P
>*)cmd
);
113 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
119 * List dependencies from the mach-o header at headerAddr
120 * in the same format as 'otool -L'
122 template <typename A
>
123 void list_dependencies(const char *dylib
, void *headerAddr
, uint8_t print_dylib_versions
) {
124 typedef typename
A::P P
;
125 typedef typename
A::P::E E
;
127 std::vector
< macho_load_command
<P
>* > cmds
;
128 cmds
= get_load_cmds
<A
>(headerAddr
);
129 for(typename
std::vector
<macho_load_command
<P
>*>::iterator it
= cmds
.begin(); it
!= cmds
.end(); ++it
) {
130 uint32_t cmdType
= (*it
)->cmd();
131 if (cmdType
== LC_LOAD_DYLIB
||
132 cmdType
== LC_ID_DYLIB
||
133 cmdType
== LC_LOAD_WEAK_DYLIB
||
134 cmdType
== LC_REEXPORT_DYLIB
||
135 cmdType
== LC_LOAD_UPWARD_DYLIB
) {
136 macho_dylib_command
<P
>* dylib_cmd
= (macho_dylib_command
<P
>*)*it
;
137 const char *name
= dylib_cmd
->name();
138 uint32_t compat_vers
= dylib_cmd
->compatibility_version();
139 uint32_t current_vers
= dylib_cmd
->current_version();
141 if (print_dylib_versions
) {
142 printf("\t%s", name
);
143 if ( compat_vers
!= 0xFFFFFFFF )
144 printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
146 (compat_vers
>> 8) & 0xff,
147 (compat_vers
) & 0xff,
148 (current_vers
>> 16),
149 (current_vers
>> 8) & 0xff,
150 (current_vers
) & 0xff);
154 printf("\t%s\n", name
);
161 * Print out a dylib from the shared cache, optionally including the UUID
163 template <typename A
>
164 void print_dylib(const char *dylib
, void *headerAddr
, uint64_t slide
, struct seg_callback_args
*args
) {
165 typedef typename
A::P P
;
166 typedef typename
A::P::E E
;
167 char uuid_str
[UUID_BYTES
*3];
168 uint8_t got_uuid
= 0;
171 std::vector
< macho_load_command
<P
>* > cmds
;
172 cmds
= get_load_cmds
<A
>(headerAddr
);
173 for(typename
std::vector
<macho_load_command
<P
>*>::iterator it
= cmds
.begin(); it
!= cmds
.end(); ++it
) {
174 uint32_t cmdType
= (*it
)->cmd();
175 if (cmdType
== LC_UUID
) {
176 macho_uuid_command
<P
>* uuid_cmd
= (macho_uuid_command
<P
>*)*it
;
177 const uint8_t *uuid
= uuid_cmd
->uuid();
178 sprintf(uuid_str
, "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X>",
179 uuid
[0], uuid
[1], uuid
[2], uuid
[3], uuid
[4], uuid
[5], uuid
[6], uuid
[7],
180 uuid
[8], uuid
[9], uuid
[10], uuid
[11], uuid
[12], uuid
[13], uuid
[14], uuid
[15]);
183 else if (cmdType
== LC_SEGMENT
) {
184 macho_segment_command
<P
>* seg_cmd
= (macho_segment_command
<P
>*)*it
;
185 if (strcmp(seg_cmd
->segname(), "__TEXT") == 0) {
186 vmaddr
= seg_cmd
->vmaddr();
191 if (args
->print_vmaddrs
)
192 printf("0x%08llX ", vmaddr
+slide
);
193 if (args
->print_uuids
) {
195 printf("%s ", uuid_str
);
197 printf("< no uuid in dylib > ");
199 printf("%s\n", dylib
);
203 uint64_t sLinkeditBase
= 0;
204 std::map
<uint32_t, char*> sPageToContent
;
209 static void add_linkedit(uint32_t pageStart
, uint32_t pageEnd
, char* message
)
211 for (uint32_t p
= pageStart
; p
<= pageEnd
; p
+= 4096) {
212 std::map
<uint32_t, char*>::iterator pos
= sPageToContent
.find(p
);
213 if ( pos
== sPageToContent
.end() ) {
214 sPageToContent
[p
] = strdup(message
);
217 char* oldMessage
= pos
->second
;
219 asprintf(&newMesssage
, "%s, %s", oldMessage
, message
);
220 sPageToContent
[p
] = newMesssage
;
228 * get LINKEDIT info for dylib
230 template <typename A
>
231 void process_linkedit(const char* dylib
, void* headerAddr
) {
232 typedef typename
A::P P
;
233 typedef typename
A::P::E E
;
234 // filter out symlinks by only handling first path found for each mach header
235 static std::set
<void*> seenImages
;
236 if ( seenImages
.count(headerAddr
) != 0 )
238 seenImages
.insert(headerAddr
);
239 const macho_header
<P
>* mh
= (const macho_header
<P
>*)headerAddr
;
240 uint32_t ncmds
= mh
->ncmds();
241 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((long)mh
+ sizeof(macho_header
<P
>));
242 const macho_load_command
<P
>* cmd
= cmds
;
243 for (uint32_t i
= 0; i
< ncmds
; i
++) {
244 if ( cmd
->cmd() == LC_DYLD_INFO_ONLY
) {
245 macho_dyld_info_command
<P
>* dyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
247 const char* shortName
= strrchr(dylib
, '/') + 1;
248 // add export trie info
249 if ( dyldInfo
->export_size() != 0 ) {
250 //printf("export_off=0x%X\n", dyldInfo->export_off());
251 uint32_t exportPageOffsetStart
= dyldInfo
->export_off() & (-4096);
252 uint32_t exportPageOffsetEnd
= (dyldInfo
->export_off() + dyldInfo
->export_size()) & (-4096);
253 sprintf(message
, "exports from %s", shortName
);
254 add_linkedit(exportPageOffsetStart
, exportPageOffsetEnd
, message
);
257 if ( dyldInfo
->bind_size() != 0 ) {
258 uint32_t bindPageOffsetStart
= dyldInfo
->bind_off() & (-4096);
259 uint32_t bindPageOffsetEnd
= (dyldInfo
->bind_off() + dyldInfo
->bind_size()) & (-4096);
260 sprintf(message
, "bindings from %s", shortName
);
261 add_linkedit(bindPageOffsetStart
, bindPageOffsetEnd
, message
);
263 // add lazy binding info
264 if ( dyldInfo
->lazy_bind_size() != 0 ) {
265 uint32_t lazybindPageOffsetStart
= dyldInfo
->lazy_bind_off() & (-4096);
266 uint32_t lazybindPageOffsetEnd
= (dyldInfo
->lazy_bind_off() + dyldInfo
->lazy_bind_size()) & (-4096);
267 sprintf(message
, "lazy bindings from %s", shortName
);
268 add_linkedit(lazybindPageOffsetStart
, lazybindPageOffsetEnd
, message
);
270 // add weak binding info
271 if ( dyldInfo
->weak_bind_size() != 0 ) {
272 uint32_t weakbindPageOffsetStart
= dyldInfo
->weak_bind_off() & (-4096);
273 uint32_t weakbindPageOffsetEnd
= (dyldInfo
->weak_bind_off() + dyldInfo
->weak_bind_size()) & (-4096);
274 sprintf(message
, "weak bindings from %s", shortName
);
275 add_linkedit(weakbindPageOffsetStart
, weakbindPageOffsetEnd
, message
);
278 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
285 * This callback is used with dsc_iterator, and called once for each segment in the target shared cache
287 template <typename A
>
288 void segment_callback(const char *dylib
, const char *segName
, uint64_t offset
, uint64_t sizem
,
289 uint64_t mappedAddress
, uint64_t slide
, void *userData
) {
290 typedef typename
A::P P
;
291 typedef typename
A::P::E E
;
292 struct seg_callback_args
*args
= (struct seg_callback_args
*)userData
;
293 if (strncmp(segName
, "__TEXT", 6) == 0) {
294 int target_match
= args
->target_path
? (strcmp(args
->target_path
, dylib
) == 0) : 0;
295 if (!args
->target_path
|| target_match
) {
297 args
->target_found
= 1;
299 void *headerAddr
= (void*)((long)args
->mapped_cache
+ (long)offset
);
301 case OP_LIST_DEPENDENCIES
:
302 list_dependencies
<A
>(dylib
, headerAddr
, args
->print_dylib_versions
);
305 print_dylib
<A
>(dylib
, headerAddr
, slide
, args
);
307 case OP_LIST_LINKEDIT
:
308 process_linkedit
<A
>(dylib
, headerAddr
);
314 else if (strncmp(segName
, "__LINKEDIT", 6) == 0) {
315 sLinkeditBase
= mappedAddress
- offset
;
322 int main (int argc
, char **argv
) {
323 struct seg_callback_args args
;
324 const char *shared_cache_path
= NULL
;
329 bool print_slide_info
= false;
331 args
.target_path
= NULL
;
333 args
.print_uuids
= 0;
334 args
.print_vmaddrs
= 0;
335 args
.print_dylib_versions
= 0;
336 args
.target_found
= 0;
338 for (uint32_t optind
= 1; optind
< argc
; optind
++) {
339 char *opt
= argv
[optind
];
341 if (strcmp(opt
, "-list") == 0) {
343 fprintf(stderr
, "Error: select one of -list or -dependents\n");
347 args
.op
= OP_LIST_DYLIBS
;
348 } else if (strcmp(opt
, "-dependents") == 0) {
350 fprintf(stderr
, "Error: select one of -list or -dependents\n");
354 if (!(++optind
< argc
)) {
355 fprintf(stderr
, "Error: option -depdendents requires an argument\n");
359 args
.op
= OP_LIST_DEPENDENCIES
;
360 args
.target_path
= argv
[optind
];
361 } else if (strcmp(opt
, "-uuid") == 0) {
362 args
.print_uuids
= 1;
363 } else if (strcmp(opt
, "-versions") == 0) {
364 args
.print_dylib_versions
= 1;
365 } else if (strcmp(opt
, "-vmaddr") == 0) {
366 args
.print_vmaddrs
= 1;
367 } else if (strcmp(opt
, "-linkedit") == 0) {
368 args
.op
= OP_LIST_LINKEDIT
;
369 } else if (strcmp(opt
, "-slide_info") == 0) {
370 print_slide_info
= true;
372 fprintf(stderr
, "Error: unrecognized option %s\n", opt
);
377 shared_cache_path
= opt
;
381 if ( !print_slide_info
) {
382 if (args
.op
== OP_NULL
) {
383 fprintf(stderr
, "Error: select one of -list or -dependents\n");
388 if (args
.print_uuids
&& args
.op
!= OP_LIST_DYLIBS
)
389 fprintf(stderr
, "Warning: -uuid option ignored outside of -list mode\n");
390 if (args
.print_vmaddrs
&& args
.op
!= OP_LIST_DYLIBS
)
391 fprintf(stderr
, "Warning: -vmaddr option ignored outside of -list mode\n");
392 if (args
.print_dylib_versions
&& args
.op
!= OP_LIST_DEPENDENCIES
)
393 fprintf(stderr
, "Warning: -versions option ignored outside of -dependents mode\n");
395 if (args
.op
== OP_LIST_DEPENDENCIES
&& !args
.target_path
) {
396 fprintf(stderr
, "Error: -dependents given, but no dylib path specified\n");
402 if (!shared_cache_path
)
403 shared_cache_path
= default_shared_cache_path();
405 if (stat(shared_cache_path
, &statbuf
)) {
406 fprintf(stderr
, "Error: stat failed for dyld shared cache at %s\n", shared_cache_path
);
410 cache_fd
= open(shared_cache_path
, O_RDONLY
);
412 fprintf(stderr
, "Error: failed to open shared cache file at %s\n", shared_cache_path
);
415 mapped_cache
= mmap(NULL
, statbuf
.st_size
, PROT_READ
, MAP_PRIVATE
, cache_fd
, 0);
416 if (mapped_cache
== MAP_FAILED
) {
417 fprintf(stderr
, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_path
, errno
);
421 if ( print_slide_info
) {
422 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)mapped_cache
;
423 if ( (strcmp(header
->magic(), "dyld_v1 x86_64") != 0)
424 && (strcmp(header
->magic(), "dyld_v1 armv6") != 0)
425 && (strcmp(header
->magic(), "dyld_v1 armv7") != 0)
426 && (strcmp(header
->magic(), "dyld_v1 armv7f") != 0)
427 && (strcmp(header
->magic(), "dyld_v1 armv7k") != 0) ) {
428 fprintf(stderr
, "Error: unrecognized dyld shared cache magic or arch does not support sliding\n");
431 if ( header
->slideInfoOffset() == 0 ) {
432 fprintf(stderr
, "Error: dyld shared cache does not contain slide info\n");
435 const dyldCacheFileMapping
<LittleEndian
>* mappings
= (dyldCacheFileMapping
<LittleEndian
>*)((char*)mapped_cache
+ header
->mappingOffset());
436 const dyldCacheFileMapping
<LittleEndian
>* dataMapping
= &mappings
[1];
437 uint64_t dataStartAddress
= dataMapping
->address();
438 uint64_t dataSize
= dataMapping
->size();
439 const dyldCacheSlideInfo
<LittleEndian
>* slideInfoHeader
= (dyldCacheSlideInfo
<LittleEndian
>*)((char*)mapped_cache
+header
->slideInfoOffset());
440 printf("slide info version=%d\n", slideInfoHeader
->version());
441 printf("toc_count=%d, data page count=%lld\n", slideInfoHeader
->toc_count(), dataSize
/4096);
442 const dyldCacheSlideInfoEntry
* entries
= (dyldCacheSlideInfoEntry
*)((char*)slideInfoHeader
+ slideInfoHeader
->entries_offset());
443 for(int i
=0; i
< slideInfoHeader
->toc_count(); ++i
) {
444 printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress
+ i
*4096, i
, slideInfoHeader
->toc(i
));
445 const dyldCacheSlideInfoEntry
* entry
= &entries
[slideInfoHeader
->toc(i
)];
446 for(int j
=0; j
< slideInfoHeader
->entries_size(); ++j
)
447 printf("%02X", entry
->bits
[j
]);
454 segment_callback_t callback
;
455 if ( strcmp((char*)mapped_cache
, "dyld_v1 i386") == 0 )
456 callback
= segment_callback
<x86
>;
457 else if ( strcmp((char*)mapped_cache
, "dyld_v1 x86_64") == 0 )
458 callback
= segment_callback
<x86_64
>;
459 else if ( strcmp((char*)mapped_cache
, "dyld_v1 armv5") == 0 )
460 callback
= segment_callback
<arm
>;
461 else if ( strcmp((char*)mapped_cache
, "dyld_v1 armv6") == 0 )
462 callback
= segment_callback
<arm
>;
463 else if ( strcmp((char*)mapped_cache
, "dyld_v1 armv7") == 0 )
464 callback
= segment_callback
<arm
>;
465 else if ( strcmp((char*)mapped_cache
, "dyld_v1 armv7f") == 0 )
466 callback
= segment_callback
<arm
>;
467 else if ( strcmp((char*)mapped_cache
, "dyld_v1 armv7k") == 0 )
468 callback
= segment_callback
<arm
>;
470 fprintf(stderr
, "Error: unrecognized dyld shared cache magic.\n");
474 args
.mapped_cache
= mapped_cache
;
477 // Shim to allow building for the host
478 void *argsPtr
= &args
;
479 dyld_shared_cache_iterate_segments_with_slide(mapped_cache
,
480 ^(const char* dylib
, const char* segName
, uint64_t offset
, uint64_t size
, uint64_t mappedddress
, uint64_t slide
) {
481 (callback
)(dylib
, segName
, offset
, size
, mappedddress
, slide
, argsPtr
);
484 dyld_shared_cache_iterate_segments_with_slide_nb(mapped_cache
, callback
, &args
);
487 if (args
.op
== OP_LIST_LINKEDIT
) {
488 // dump -linkedit information
489 for (std::map
<uint32_t, char*>::iterator it
= sPageToContent
.begin(); it
!= sPageToContent
.end(); ++it
) {
490 printf("0x%0llX %s\n", sLinkeditBase
+it
->first
, it
->second
);
495 if (args
.target_path
&& !args
.target_found
) {
496 fprintf(stderr
, "Error: could not find '%s' in the shared cache at\n %s\n", args
.target_path
, shared_cache_path
);