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";
82 return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"rosetta";
83 #elif __ARM_ARCH_5TEJ__
84 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv5";
86 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv6";
88 return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME
"armv7";
90 #error unsupported architecture
95 * Get a vector of all the load commands from the header pointed to by headerAddr
98 std::vector
<macho_load_command
<typename
A::P
>* > get_load_cmds(void *headerAddr
) {
99 typedef typename
A::P P
;
100 typedef typename
A::P::E E
;
102 std::vector
<macho_load_command
<P
>* > cmd_vector
;
104 const macho_header
<P
>* mh
= (const macho_header
<P
>*)headerAddr
;
105 uint32_t ncmds
= mh
->ncmds();
106 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((long)mh
+ sizeof(macho_header
<P
>));
107 const macho_load_command
<P
>* cmd
= cmds
;
109 for (uint32_t i
= 0; i
< ncmds
; i
++) {
110 cmd_vector
.push_back((macho_load_command
<P
>*)cmd
);
111 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
117 * List dependencies from the mach-o header at headerAddr
118 * in the same format as 'otool -L'
120 template <typename A
>
121 void list_dependencies(const char *dylib
, void *headerAddr
, uint8_t print_dylib_versions
) {
122 typedef typename
A::P P
;
123 typedef typename
A::P::E E
;
125 std::vector
< macho_load_command
<P
>* > cmds
;
126 cmds
= get_load_cmds
<A
>(headerAddr
);
127 for(typename
std::vector
<macho_load_command
<P
>*>::iterator it
= cmds
.begin(); it
!= cmds
.end(); ++it
) {
128 uint32_t cmdType
= (*it
)->cmd();
129 if (cmdType
== LC_LOAD_DYLIB
||
130 cmdType
== LC_ID_DYLIB
||
131 cmdType
== LC_LOAD_WEAK_DYLIB
||
132 cmdType
== LC_REEXPORT_DYLIB
||
133 cmdType
== LC_LOAD_UPWARD_DYLIB
) {
134 macho_dylib_command
<P
>* dylib_cmd
= (macho_dylib_command
<P
>*)*it
;
135 const char *name
= dylib_cmd
->name();
136 uint32_t compat_vers
= dylib_cmd
->compatibility_version();
137 uint32_t current_vers
= dylib_cmd
->current_version();
139 if (print_dylib_versions
) {
140 printf("\t%s (compatibility version %u.%u.%u, current version %u.%u.%u)\n",
143 (compat_vers
>> 8) & 0xff,
144 (compat_vers
) & 0xff,
145 (current_vers
>> 16),
146 (current_vers
>> 8) & 0xff,
147 (current_vers
) & 0xff);
149 printf("\t%s\n", name
);
156 * Print out a dylib from the shared cache, optionally including the UUID
158 template <typename A
>
159 void print_dylib(const char *dylib
, void *headerAddr
, uint64_t slide
, struct seg_callback_args
*args
) {
160 typedef typename
A::P P
;
161 typedef typename
A::P::E E
;
162 char uuid_str
[UUID_BYTES
*3];
163 uint8_t got_uuid
= 0;
166 std::vector
< macho_load_command
<P
>* > cmds
;
167 cmds
= get_load_cmds
<A
>(headerAddr
);
168 for(typename
std::vector
<macho_load_command
<P
>*>::iterator it
= cmds
.begin(); it
!= cmds
.end(); ++it
) {
169 uint32_t cmdType
= (*it
)->cmd();
170 if (cmdType
== LC_UUID
) {
171 macho_uuid_command
<P
>* uuid_cmd
= (macho_uuid_command
<P
>*)*it
;
172 const uint8_t *uuid
= uuid_cmd
->uuid();
173 sprintf(uuid_str
, "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X>",
174 uuid
[0], uuid
[1], uuid
[2], uuid
[3], uuid
[4], uuid
[5], uuid
[6], uuid
[7],
175 uuid
[8], uuid
[9], uuid
[10], uuid
[11], uuid
[12], uuid
[13], uuid
[14], uuid
[15]);
178 else if (cmdType
== LC_SEGMENT
) {
179 macho_segment_command
<P
>* seg_cmd
= (macho_segment_command
<P
>*)*it
;
180 if (strcmp(seg_cmd
->segname(), "__TEXT") == 0) {
181 vmaddr
= seg_cmd
->vmaddr();
186 if (args
->print_vmaddrs
)
187 printf("0x%08llX ", vmaddr
+slide
);
188 if (args
->print_uuids
) {
190 printf("%s ", uuid_str
);
192 printf("< no uuid in dylib > ");
194 printf("%s\n", dylib
);
198 uint64_t sLinkeditBase
= 0;
199 std::map
<uint32_t, char*> sPageToContent
;
204 static void add_linkedit(uint32_t pageStart
, uint32_t pageEnd
, char* message
)
206 for (uint32_t p
= pageStart
; p
<= pageEnd
; p
+= 4096) {
207 std::map
<uint32_t, char*>::iterator pos
= sPageToContent
.find(p
);
208 if ( pos
== sPageToContent
.end() ) {
209 sPageToContent
[p
] = strdup(message
);
212 char* oldMessage
= pos
->second
;
214 asprintf(&newMesssage
, "%s, %s", oldMessage
, message
);
215 sPageToContent
[p
] = newMesssage
;
223 * get LINKEDIT info for dylib
225 template <typename A
>
226 void process_linkedit(const char* dylib
, void* headerAddr
) {
227 typedef typename
A::P P
;
228 typedef typename
A::P::E E
;
229 // filter out symlinks by only handling first path found for each mach header
230 static std::set
<void*> seenImages
;
231 if ( seenImages
.count(headerAddr
) != 0 )
233 seenImages
.insert(headerAddr
);
234 const macho_header
<P
>* mh
= (const macho_header
<P
>*)headerAddr
;
235 uint32_t ncmds
= mh
->ncmds();
236 const macho_load_command
<P
>* const cmds
= (macho_load_command
<P
>*)((long)mh
+ sizeof(macho_header
<P
>));
237 const macho_load_command
<P
>* cmd
= cmds
;
238 for (uint32_t i
= 0; i
< ncmds
; i
++) {
239 if ( cmd
->cmd() == LC_DYLD_INFO_ONLY
) {
240 macho_dyld_info_command
<P
>* dyldInfo
= (macho_dyld_info_command
<P
>*)cmd
;
242 const char* shortName
= strrchr(dylib
, '/') + 1;
243 // add export trie info
244 if ( dyldInfo
->export_size() != 0 ) {
245 //printf("export_off=0x%X\n", dyldInfo->export_off());
246 uint32_t exportPageOffsetStart
= dyldInfo
->export_off() & (-4096);
247 uint32_t exportPageOffsetEnd
= (dyldInfo
->export_off() + dyldInfo
->export_size()) & (-4096);
248 sprintf(message
, "exports from %s", shortName
);
249 add_linkedit(exportPageOffsetStart
, exportPageOffsetEnd
, message
);
252 if ( dyldInfo
->bind_size() != 0 ) {
253 uint32_t bindPageOffsetStart
= dyldInfo
->bind_off() & (-4096);
254 uint32_t bindPageOffsetEnd
= (dyldInfo
->bind_off() + dyldInfo
->bind_size()) & (-4096);
255 sprintf(message
, "bindings from %s", shortName
);
256 add_linkedit(bindPageOffsetStart
, bindPageOffsetEnd
, message
);
258 // add lazy binding info
259 if ( dyldInfo
->lazy_bind_size() != 0 ) {
260 uint32_t lazybindPageOffsetStart
= dyldInfo
->lazy_bind_off() & (-4096);
261 uint32_t lazybindPageOffsetEnd
= (dyldInfo
->lazy_bind_off() + dyldInfo
->lazy_bind_size()) & (-4096);
262 sprintf(message
, "lazy bindings from %s", shortName
);
263 add_linkedit(lazybindPageOffsetStart
, lazybindPageOffsetEnd
, message
);
265 // add weak binding info
266 if ( dyldInfo
->weak_bind_size() != 0 ) {
267 uint32_t weakbindPageOffsetStart
= dyldInfo
->weak_bind_off() & (-4096);
268 uint32_t weakbindPageOffsetEnd
= (dyldInfo
->weak_bind_off() + dyldInfo
->weak_bind_size()) & (-4096);
269 sprintf(message
, "weak bindings from %s", shortName
);
270 add_linkedit(weakbindPageOffsetStart
, weakbindPageOffsetEnd
, message
);
273 cmd
= (const macho_load_command
<P
>*)(((uint8_t*)cmd
)+cmd
->cmdsize());
280 * This callback is used with dsc_iterator, and called once for each segment in the target shared cache
282 template <typename A
>
283 void segment_callback(const char *dylib
, const char *segName
, uint64_t offset
, uint64_t sizem
,
284 uint64_t mappedAddress
, uint64_t slide
, void *userData
) {
285 typedef typename
A::P P
;
286 typedef typename
A::P::E E
;
287 struct seg_callback_args
*args
= (struct seg_callback_args
*)userData
;
288 if (strncmp(segName
, "__TEXT", 6) == 0) {
289 int target_match
= args
->target_path
? (strcmp(args
->target_path
, dylib
) == 0) : 0;
290 if (!args
->target_path
|| target_match
) {
292 args
->target_found
= 1;
294 void *headerAddr
= (void*)((long)args
->mapped_cache
+ (long)offset
);
296 case OP_LIST_DEPENDENCIES
:
297 list_dependencies
<A
>(dylib
, headerAddr
, args
->print_dylib_versions
);
300 print_dylib
<A
>(dylib
, headerAddr
, slide
, args
);
302 case OP_LIST_LINKEDIT
:
303 process_linkedit
<A
>(dylib
, headerAddr
);
309 else if (strncmp(segName
, "__LINKEDIT", 6) == 0) {
310 sLinkeditBase
= mappedAddress
- offset
;
317 int main (int argc
, char **argv
) {
318 struct seg_callback_args args
;
319 const char *shared_cache_path
= NULL
;
324 bool print_slide_info
= false;
326 args
.target_path
= NULL
;
328 args
.print_uuids
= 0;
329 args
.print_vmaddrs
= 0;
330 args
.print_dylib_versions
= 0;
331 args
.target_found
= 0;
333 for (uint32_t optind
= 1; optind
< argc
; optind
++) {
334 char *opt
= argv
[optind
];
336 if (strcmp(opt
, "-list") == 0) {
338 fprintf(stderr
, "Error: select one of -list or -dependents\n");
342 args
.op
= OP_LIST_DYLIBS
;
343 } else if (strcmp(opt
, "-dependents") == 0) {
345 fprintf(stderr
, "Error: select one of -list or -dependents\n");
349 if (!(++optind
< argc
)) {
350 fprintf(stderr
, "Error: option -depdendents requires an argument\n");
354 args
.op
= OP_LIST_DEPENDENCIES
;
355 args
.target_path
= argv
[optind
];
356 } else if (strcmp(opt
, "-uuid") == 0) {
357 args
.print_uuids
= 1;
358 } else if (strcmp(opt
, "-versions") == 0) {
359 args
.print_dylib_versions
= 1;
360 } else if (strcmp(opt
, "-vmaddr") == 0) {
361 args
.print_vmaddrs
= 1;
362 } else if (strcmp(opt
, "-linkedit") == 0) {
363 args
.op
= OP_LIST_LINKEDIT
;
364 } else if (strcmp(opt
, "-slide_info") == 0) {
365 print_slide_info
= true;
367 fprintf(stderr
, "Error: unrecognized option %s\n", opt
);
372 shared_cache_path
= opt
;
376 if ( !print_slide_info
) {
377 if (args
.op
== OP_NULL
) {
378 fprintf(stderr
, "Error: select one of -list or -dependents\n");
383 if (args
.print_uuids
&& args
.op
!= OP_LIST_DYLIBS
)
384 fprintf(stderr
, "Warning: -uuid option ignored outside of -list mode\n");
385 if (args
.print_vmaddrs
&& args
.op
!= OP_LIST_DYLIBS
)
386 fprintf(stderr
, "Warning: -vmaddr option ignored outside of -list mode\n");
387 if (args
.print_dylib_versions
&& args
.op
!= OP_LIST_DEPENDENCIES
)
388 fprintf(stderr
, "Warning: -versions option ignored outside of -dependents mode\n");
390 if (args
.op
== OP_LIST_DEPENDENCIES
&& !args
.target_path
) {
391 fprintf(stderr
, "Error: -dependents given, but no dylib path specified\n");
397 if (!shared_cache_path
)
398 shared_cache_path
= default_shared_cache_path();
400 if (stat(shared_cache_path
, &statbuf
)) {
401 fprintf(stderr
, "Error: stat failed for dyld shared cache at %s\n", shared_cache_path
);
405 cache_fd
= open(shared_cache_path
, O_RDONLY
);
407 fprintf(stderr
, "Error: failed to open shared cache file at %s\n", shared_cache_path
);
410 mapped_cache
= mmap(NULL
, statbuf
.st_size
, PROT_READ
, MAP_PRIVATE
, cache_fd
, 0);
411 if (mapped_cache
== MAP_FAILED
) {
412 fprintf(stderr
, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_path
, errno
);
416 if ( print_slide_info
) {
417 const dyldCacheHeader
<LittleEndian
>* header
= (dyldCacheHeader
<LittleEndian
>*)mapped_cache
;
418 if ( (strcmp(header
->magic(), "dyld_v1 x86_64") != 0)
419 && (strcmp(header
->magic(), "dyld_v1 armv6") != 0)
420 && (strcmp(header
->magic(), "dyld_v1 armv7") != 0) ) {
421 fprintf(stderr
, "Error: unrecognized dyld shared cache magic or arch does not support sliding\n");
424 if ( header
->slideInfoOffset() == 0 ) {
425 fprintf(stderr
, "Error: dyld shared cache does not contain slide info\n");
428 const dyldCacheFileMapping
<LittleEndian
>* mappings
= (dyldCacheFileMapping
<LittleEndian
>*)((char*)mapped_cache
+ header
->mappingOffset());
429 const dyldCacheFileMapping
<LittleEndian
>* dataMapping
= &mappings
[1];
430 uint64_t dataStartAddress
= dataMapping
->address();
431 uint64_t dataSize
= dataMapping
->size();
432 const dyldCacheSlideInfo
<LittleEndian
>* slideInfoHeader
= (dyldCacheSlideInfo
<LittleEndian
>*)((char*)mapped_cache
+header
->slideInfoOffset());
433 printf("slide info version=%d\n", slideInfoHeader
->version());
434 printf("toc_count=%d, data page count=%lld\n", slideInfoHeader
->toc_count(), dataSize
/4096);
435 const dyldCacheSlideInfoEntry
* entries
= (dyldCacheSlideInfoEntry
*)((char*)slideInfoHeader
+ slideInfoHeader
->entries_offset());
436 for(int i
=0; i
< slideInfoHeader
->toc_count(); ++i
) {
437 printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress
+ i
*4096, i
, slideInfoHeader
->toc(i
));
438 const dyldCacheSlideInfoEntry
* entry
= &entries
[slideInfoHeader
->toc(i
)];
439 for(int j
=0; j
< slideInfoHeader
->entries_size(); ++j
)
440 printf("%02X", entry
->bits
[j
]);
447 segment_callback_t callback
;
448 if ( strcmp((char*)mapped_cache
, "dyld_v1 i386") == 0 )
449 callback
= segment_callback
<x86
>;
450 else if ( strcmp((char*)mapped_cache
, "dyld_v1 x86_64") == 0 )
451 callback
= segment_callback
<x86_64
>;
452 else if ( strcmp((char*)mapped_cache
, "dyld_v1 ppc") == 0 )
453 callback
= segment_callback
<ppc
>;
454 else if ( strcmp((char*)mapped_cache
, "dyld_v1 armv5") == 0 )
455 callback
= segment_callback
<arm
>;
456 else if ( strcmp((char*)mapped_cache
, "dyld_v1 armv6") == 0 )
457 callback
= segment_callback
<arm
>;
458 else if ( strcmp((char*)mapped_cache
, "dyld_v1 armv7") == 0 )
459 callback
= segment_callback
<arm
>;
461 fprintf(stderr
, "Error: unrecognized dyld shared cache magic.\n");
465 args
.mapped_cache
= mapped_cache
;
468 // Shim to allow building for the host
469 void *argsPtr
= &args
;
470 dyld_shared_cache_iterate_segments_with_slide(mapped_cache
,
471 ^(const char* dylib
, const char* segName
, uint64_t offset
, uint64_t size
, uint64_t mappedddress
, uint64_t slide
) {
472 (callback
)(dylib
, segName
, offset
, size
, mappedddress
, slide
, argsPtr
);
475 dyld_shared_cache_iterate_segments_with_slide_nb(mapped_cache
, callback
, &args
);
478 if (args
.op
== OP_LIST_LINKEDIT
) {
479 // dump -linkedit information
480 for (std::map
<uint32_t, char*>::iterator it
= sPageToContent
.begin(); it
!= sPageToContent
.end(); ++it
) {
481 printf("0x%0llX %s\n", sLinkeditBase
+it
->first
, it
->second
);
486 if (args
.target_path
&& !args
.target_found
) {
487 fprintf(stderr
, "Error: could not find '%s' in the shared cache at\n %s\n", args
.target_path
, shared_cache_path
);