1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2008-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@
25 #include <sys/types.h>
37 #include <unordered_set>
41 #include "MachOFile.h"
42 #include "MachOLoaded.h"
43 #include "MachOAnalyzer.h"
44 #include "ClosureFileSystemPhysical.h"
46 static bool printSharedRegion
= false;
47 static bool printFunctionStarts
= false;
48 static bool printDataCode
= false;
51 static void versionToString(uint32_t value
, char buffer
[32])
54 strcpy(buffer
, "n/a");
55 else if ( value
& 0xFF )
56 sprintf(buffer
, "%d.%d.%d", value
>> 16, (value
>> 8) & 0xFF, value
& 0xFF);
58 sprintf(buffer
, "%d.%d", value
>> 16, (value
>> 8) & 0xFF);
61 static void printPlatforms(const dyld3::MachOAnalyzer
* ma
)
63 printf(" -platform:\n");
64 printf(" platform minOS sdk\n");
65 ma
->forEachSupportedPlatform(^(dyld3::Platform platform
, uint32_t minOS
, uint32_t sdk
) {
68 versionToString(minOS
, osVers
);
69 versionToString(sdk
, sdkVers
);
70 printf(" %15s %-7s %-7s\n", dyld3::MachOFile::platformName(platform
), osVers
, sdkVers
);
74 static void printSegments(const dyld3::MachOAnalyzer
* ma
)
76 printf(" -segments:\n");
77 printf(" load-offset segment section sect-size seg-size perm\n");
78 __block
const char* lastSegName
= "";
79 __block
uint64_t firstSegVmAddr
= 0;
80 ma
->forEachSection(^(const dyld3::MachOFile::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& stop
) {
81 if ( lastSegName
[0] == '\0' )
82 firstSegVmAddr
= sectInfo
.segInfo
.vmAddr
;
83 if ( strcmp(lastSegName
, sectInfo
.segInfo
.segName
) != 0 ) {
84 char r
= (sectInfo
.segInfo
.protections
& VM_PROT_READ
) ? 'r' : '.';
85 char w
= (sectInfo
.segInfo
.protections
& VM_PROT_WRITE
) ? 'w' : '.';
86 char x
= (sectInfo
.segInfo
.protections
& VM_PROT_EXECUTE
) ? 'x' : '.';
87 printf(" 0x%08llX %-12s %6lluKB %c%c%c\n", sectInfo
.segInfo
.vmAddr
- firstSegVmAddr
, sectInfo
.segInfo
.segName
, sectInfo
.segInfo
.vmSize
/1024, r
, w
, x
);
88 lastSegName
= sectInfo
.segInfo
.segName
;
90 printf(" 0x%08llX %-16s %6llu\n", sectInfo
.sectAddr
-firstSegVmAddr
, sectInfo
.sectName
, sectInfo
.sectSize
);
97 static void printDependents(const dyld3::MachOAnalyzer
* ma
)
99 printf(" -dependents:\n");
100 printf(" attributes load path\n");
101 ma
->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
102 const char* attribute
= "";
104 attribute
= "weak_import";
105 else if ( isReExport
)
106 attribute
= "re-export";
108 attribute
= "upward";
109 printf(" %-12s %s\n", attribute
, loadPath
);
113 static const char* rebaseTypeName(uint8_t type
)
116 case REBASE_TYPE_POINTER
:
117 return "rebase pointer";
118 case REBASE_TYPE_TEXT_ABSOLUTE32
:
119 return "rebase text abs32";
120 case REBASE_TYPE_TEXT_PCREL32
:
121 return "rebase text rel32";
123 return "!!unknown!!";
126 static const char* bindTypeName(uint8_t type
)
129 case BIND_TYPE_POINTER
:
130 return "bind pointer";
131 case BIND_TYPE_TEXT_ABSOLUTE32
:
132 return "bind text abs32";
133 case BIND_TYPE_TEXT_PCREL32
:
134 return "bind text rel32";
136 return "!!unknown!!";
139 static const char* pointerFormat(uint16_t format
)
142 case DYLD_CHAINED_PTR_ARM64E
:
143 return "authenticated arm64e";
144 case DYLD_CHAINED_PTR_64
:
145 return "generic 64-bit";
146 case DYLD_CHAINED_PTR_32
:
147 return "generic 32-bit";
148 case DYLD_CHAINED_PTR_32_CACHE
:
149 return "32-bit for dyld cache";
154 static void printChains(const dyld3::MachOAnalyzer
* ma
)
157 ma
->withChainStarts(diag
, 0, ^(const dyld_chained_starts_in_image
* starts
) {
158 for (int i
=0; i
< starts
->seg_count
; ++i
) {
159 if ( starts
->seg_info_offset
[i
] == 0 )
161 const dyld_chained_starts_in_segment
* seg
= (dyld_chained_starts_in_segment
*)((uint8_t*)starts
+ starts
->seg_info_offset
[i
]);
162 if ( seg
->page_count
== 0 )
164 printf("seg[%d]:\n", i
);
165 printf(" page_size: 0x%04X\n", seg
->page_size
);
166 printf(" pointer_format: %d (%s)\n", seg
->pointer_format
, pointerFormat(seg
->pointer_format
));
167 printf(" segment_offset: 0x%08llX\n", seg
->segment_offset
);
168 printf(" max_pointer: 0x%08X\n", seg
->max_valid_pointer
);
169 printf(" pages: %d\n", seg
->page_count
);
170 for (int p
=0; p
< seg
->page_count
; ++p
) {
171 printf(" start[% 2d]: 0x%04X\n", p
, seg
->page_start
[p
]);
180 std::string sectName
;
183 uint64_t targetValue
;
184 const char* targetDylib
;
185 const char* targetSymbolName
;
186 uint64_t targetAddend
;
187 bool targetWeakImport
;
192 static const char* ordinalName(const dyld3::MachOAnalyzer
* ma
, int libraryOrdinal
)
194 static int sLastOrdinal
= -100;
195 static const char* sLastString
= nullptr;
196 if ( libraryOrdinal
> 0 ) {
197 if ( libraryOrdinal
!= sLastOrdinal
) {
198 const char* path
= ma
->dependentDylibLoadPath(libraryOrdinal
-1);
199 if ( path
== nullptr )
200 return "ordinal-too-large";
201 const char* leafName
= path
;
202 if ( const char* lastSlash
= strrchr(path
, '/') )
203 leafName
= lastSlash
+1;
204 sLastOrdinal
= libraryOrdinal
;
205 sLastString
= leafName
;
210 switch ( libraryOrdinal
) {
211 case BIND_SPECIAL_DYLIB_SELF
:
213 case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE
:
214 return "main-executable";
215 case BIND_SPECIAL_DYLIB_FLAT_LOOKUP
:
216 return "flat-namespace";
217 case BIND_SPECIAL_DYLIB_WEAK_LOOKUP
:
218 return "weak-coalesce";
221 return "unknown-ordinal";
228 SectionFinder(const dyld3::MachOAnalyzer
* ma
);
229 const char* segmentName(uint64_t vmOffset
) const;
230 const char* sectionName(uint64_t vmOffset
) const;
231 uint64_t baseAddress() const { return _baseAddress
; }
234 void updateLastSection(uint64_t vmOffset
) const;
236 const dyld3::MachOAnalyzer
* _ma
;
237 uint64_t _baseAddress
;
238 mutable dyld3::MachOFile::SectionInfo _lastSection
;
239 mutable char _lastSectName
[20];
242 SectionFinder::SectionFinder(const dyld3::MachOAnalyzer
* ma
)
245 _baseAddress
= ma
->preferredLoadAddress();
246 _lastSection
.sectAddr
= 0;
247 _lastSection
.sectSize
= 0;
250 void SectionFinder::updateLastSection(uint64_t vmOffset
) const
252 uint64_t vmAddr
= _baseAddress
+ vmOffset
;
253 if ( (vmAddr
< _lastSection
.sectAddr
) || (vmAddr
>= _lastSection
.sectAddr
+_lastSection
.sectSize
) ) {
254 _ma
->forEachSection(^(const dyld3::MachOFile::SectionInfo
& sectInfo
, bool malformedSectionRange
, bool& sectStop
) {
255 if ( (sectInfo
.sectAddr
<= vmAddr
) && (vmAddr
< sectInfo
.sectAddr
+sectInfo
.sectSize
) ) {
256 _lastSection
= sectInfo
;
257 strcpy(_lastSectName
, _lastSection
.sectName
);
264 const char* SectionFinder::segmentName(uint64_t vmOffset
) const
266 updateLastSection(vmOffset
);
267 return _lastSection
.segInfo
.segName
;
270 const char* SectionFinder::sectionName(uint64_t vmOffset
) const
272 updateLastSection(vmOffset
);
273 return _lastSectName
;
278 static void printPreloadChainedFixups(const dyld3::MachOAnalyzer
* ma
)
280 printf(" segment section address type (dvrsty addr key) target\n");
281 SectionFinder
namer(ma
);
282 uint64_t sectionSize
;
283 const dyld_chained_starts_offsets
* startsSection
= (dyld_chained_starts_offsets
*)ma
->findSectionContent("__TEXT", "__chain_starts", sectionSize
);
284 if ( startsSection
!= nullptr ) {
285 switch (startsSection
->pointer_format
) {
286 case DYLD_CHAINED_PTR_32_FIRMWARE
:
287 for (uint32_t startIndex
=0; startIndex
< startsSection
->starts_count
; ++startIndex
) {
288 const dyld_chained_ptr_32_firmware_rebase
* p
= (dyld_chained_ptr_32_firmware_rebase
*)(((uint8_t*)ma
)+ startsSection
->chain_starts
[startIndex
]);
291 uint64_t vmOffset
= ((uint8_t*)p
- (uint8_t*)ma
);
292 printf(" %-12s %-16s 0x%08llX %16s 0x%08X\n",
293 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), namer
.baseAddress()+vmOffset
,
294 "rebase pointer", p
->target
);
295 done
= (p
->next
== 0);
310 const char* symbolName
;
316 static void printChainedFixups(const dyld3::MachOAnalyzer
* ma
)
318 // build array of targets
319 __block Diagnostics diag
;
320 __block
std::vector
<FixupTarget
> targets
;
321 ma
->forEachChainedFixupTarget(diag
, ^(int libOrdinal
, const char* symbolName
, uint64_t addend
, bool weakImport
, bool& stop
) {
324 target
.dylib
= ordinalName(ma
, libOrdinal
);
325 target
.symbolName
= symbolName
;
326 target
.addend
= addend
;
327 target
.weakImport
= weakImport
;
328 targets
.push_back(target
);
330 if ( diag
.hasError() )
333 uint64_t baseAddress
= ma
->preferredLoadAddress();
335 printf(" segment section address type (dvrsty addr key) target\n");
336 SectionFinder
namer(ma
);
337 ma
->withChainStarts(diag
, 0, ^(const dyld_chained_starts_in_image
* starts
) {
338 ma
->forEachFixupInAllChains(diag
, starts
, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& stop
) {
339 uint64_t vmOffset
= (uint8_t*)fixupLoc
- (uint8_t*)ma
;
340 switch (segInfo
->pointer_format
) {
341 case DYLD_CHAINED_PTR_ARM64E
:
342 if ( fixupLoc
->arm64e
.authRebase
.auth
) {
343 if ( fixupLoc
->arm64e
.authBind
.bind
) {
344 const FixupTarget
& bindTarget
= targets
[fixupLoc
->arm64e
.authBind
.ordinal
];
345 if ( bindTarget
.addend
)
346 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) %s/%s + 0x%llX\n",
347 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(), "bind authptr",
348 fixupLoc
->arm64e
.authBind
.diversity
, fixupLoc
->arm64e
.authBind
.addrDiv
,
349 fixupLoc
->arm64e
.keyName(), bindTarget
.dylib
, bindTarget
.symbolName
, bindTarget
.addend
);
351 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) %s/%s\n",
352 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(), "bind authptr",
353 fixupLoc
->arm64e
.authBind
.diversity
, fixupLoc
->arm64e
.authBind
.addrDiv
,
354 fixupLoc
->arm64e
.keyName(), bindTarget
.dylib
, bindTarget
.symbolName
);
357 uint64_t targetAddr
= fixupLoc
->arm64e
.authRebase
.target
+ baseAddress
;
358 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) 0x%08llX\n",
359 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(), "rebase authptr",
360 fixupLoc
->arm64e
.authBind
.diversity
, fixupLoc
->arm64e
.authBind
.addrDiv
,
361 fixupLoc
->arm64e
.keyName(), targetAddr
);
365 if ( fixupLoc
->arm64e
.rebase
.bind
) {
366 const FixupTarget
& bindTarget
= targets
[fixupLoc
->arm64e
.bind
.ordinal
];
367 uint64_t fullAddend
= bindTarget
.addend
+ fixupLoc
->arm64e
.signExtendedAddend();
369 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX\n",
370 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
371 "bind pointer", bindTarget
.dylib
, bindTarget
.symbolName
, fullAddend
);
373 printf(" %-12s %-16s 0x%08llX %16s %s/%s\n",
374 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
375 "bind pointer", bindTarget
.dylib
, bindTarget
.symbolName
);
378 uint64_t targetAddr
= fixupLoc
->arm64e
.unpackTarget();
379 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n",
380 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
381 "rebase pointer", targetAddr
);
385 case DYLD_CHAINED_PTR_64
:
386 if ( fixupLoc
->generic64
.rebase
.bind
) {
387 const FixupTarget
& bindTarget
= targets
[fixupLoc
->generic64
.bind
.ordinal
];
388 uint64_t fullAddend
= bindTarget
.addend
+ fixupLoc
->generic64
.signExtendedAddend();
390 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX\n",
391 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
392 "bind pointer", bindTarget
.dylib
, bindTarget
.symbolName
, fullAddend
);
394 printf(" %-12s %-16s 0x%08llX %16s %s/%s\n",
395 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
396 "bind pointer", bindTarget
.dylib
, bindTarget
.symbolName
);
399 uint64_t targetAddr
= fixupLoc
->generic64
.unpackedTarget();
400 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n",
401 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
402 "rebase pointer", targetAddr
);
405 case DYLD_CHAINED_PTR_32
:
406 if ( fixupLoc
->generic32
.rebase
.bind
) {
407 const FixupTarget
& bindTarget
= targets
[fixupLoc
->generic32
.bind
.ordinal
];
408 uint32_t fullAddend
= (uint32_t)bindTarget
.addend
+ fixupLoc
->generic32
.bind
.addend
;
410 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%X\n",
411 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
412 "bind pointer", bindTarget
.dylib
, bindTarget
.symbolName
, fullAddend
);
414 printf(" %-12s %-16s 0x%08llX %16s %s/%s\n",
415 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
416 "bind pointer", bindTarget
.dylib
, bindTarget
.symbolName
);
419 uint32_t targetAddr
= fixupLoc
->generic32
.rebase
.target
;
420 printf(" %-12s %-16s 0x%08llX %16s 0x%08X\n",
421 namer
.segmentName(vmOffset
), namer
.sectionName(vmOffset
), vmOffset
+namer
.baseAddress(),
422 "rebase pointer", targetAddr
);
426 fprintf(stderr
, "unknown pointer type %d\n", segInfo
->pointer_format
);
433 static void printOpcodeFixups(const dyld3::MachOAnalyzer
* ma
)
436 __block
std::vector
<FixupInfo
> fixups
;
437 SectionFinder
namer(ma
);
438 ma
->forEachRebase(diag
, ^(const char* opcodeName
, const dyld3::MachOLoaded::LinkEditInfo
& leInfo
, const dyld3::MachOFile::SegmentInfo segments
[],
439 bool segIndexSet
, uint32_t pointerSize
, uint8_t segIndex
, uint64_t segOffset
, uint8_t type
, bool& stop
) {
440 const dyld3::MachOFile::SegmentInfo
& segment
= segments
[segIndex
];
441 uint64_t locVmAddr
= segment
.vmAddr
+ segOffset
;
442 uint64_t runtimeOffset
= locVmAddr
- namer
.baseAddress();
443 const uint8_t* loc
= ((uint8_t*)ma
+ runtimeOffset
);
444 uint64_t value
= (pointerSize
== 8) ? *((uint64_t*)(loc
)) : *((uint32_t*)(loc
));
446 fixup
.segName
= namer
.segmentName(runtimeOffset
);
447 fixup
.sectName
= namer
.sectionName(runtimeOffset
);
448 fixup
.address
= locVmAddr
;
449 fixup
.type
= rebaseTypeName(type
);
450 fixup
.targetValue
= value
;
451 fixup
.targetDylib
= nullptr;
452 fixup
.targetSymbolName
= nullptr;
453 fixup
.targetAddend
= 0;
454 fixup
.targetWeakImport
= false;
455 fixups
.push_back(fixup
);
458 ma
->forEachBind(diag
, ^(const char* opcodeName
, const dyld3::MachOLoaded::LinkEditInfo
& leInfo
, const dyld3::MachOFile::SegmentInfo segments
[],
459 bool segIndexSet
, bool libraryOrdinalSet
, uint32_t dylibCount
, int libOrdinal
,
460 uint32_t pointerSize
, uint8_t segIndex
, uint64_t segOffset
,
461 uint8_t type
, const char* symbolName
, bool weakImport
, bool lazyBind
, uint64_t addend
, bool& stop
) {
462 const dyld3::MachOFile::SegmentInfo
& segment
= segments
[segIndex
];
463 uint64_t locVmAddr
= segment
.vmAddr
+ segOffset
;
464 uint64_t runtimeOffset
= locVmAddr
- namer
.baseAddress();
466 fixup
.segName
= namer
.segmentName(runtimeOffset
);
467 fixup
.sectName
= namer
.sectionName(runtimeOffset
);
468 fixup
.address
= locVmAddr
;
469 fixup
.type
= bindTypeName(type
);
470 fixup
.targetValue
= 0;
471 fixup
.targetDylib
= ordinalName(ma
, libOrdinal
);
472 fixup
.targetSymbolName
= symbolName
;
473 fixup
.targetAddend
= addend
;
474 fixup
.targetWeakImport
= weakImport
;
475 fixups
.push_back(fixup
);
476 },^(const char* symbolName
) {
480 std::sort(fixups
.begin(), fixups
.end(), [](const FixupInfo
& l
, const FixupInfo
& r
) {
483 if ( l
.address
== r
.address
)
484 return (l
.targetSymbolName
== nullptr);
485 return ( l
.address
< r
.address
);
488 printf(" segment section address type target\n");
489 for (const FixupInfo
& fixup
: fixups
) {
490 if ( fixup
.targetSymbolName
== nullptr )
491 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n", fixup
.segName
, fixup
.sectName
.c_str(), fixup
.address
, fixup
.type
, fixup
.targetValue
);
492 else if ( fixup
.targetAddend
!= 0 )
493 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX\n", fixup
.segName
, fixup
.sectName
.c_str(), fixup
.address
, fixup
.type
, fixup
.targetDylib
, fixup
.targetSymbolName
, fixup
.targetAddend
);
494 else if ( fixup
.targetWeakImport
)
495 printf(" %-12s %-16s 0x%08llX %16s %s/%s [weak-import]\n", fixup
.segName
, fixup
.sectName
.c_str(), fixup
.address
, fixup
.type
, fixup
.targetDylib
, fixup
.targetSymbolName
);
497 printf(" %-12s %-16s 0x%08llX %16s %s/%s\n", fixup
.segName
, fixup
.sectName
.c_str(), fixup
.address
, fixup
.type
, fixup
.targetDylib
, fixup
.targetSymbolName
);
504 static void printFixups(const dyld3::MachOAnalyzer
* ma
)
506 printf(" -fixups:\n");
507 if ( ma
->isPreload() || ma
->isStaticExecutable() ) {
508 printPreloadChainedFixups(ma
);
510 else if ( ma
->hasChainedFixups() ) {
511 printChainedFixups(ma
);
514 printOpcodeFixups(ma
);
519 static void printExports(const dyld3::MachOAnalyzer
* ma
)
521 printf(" -exports:\n");
522 printf(" offset symbol\n");
524 ma
->forEachExportedSymbol(diag
, ^(const char* symbolName
, uint64_t imageOffset
, uint64_t flags
, uint64_t other
, const char* importName
, bool& stop
) {
525 //printf("0x%08llX %s\n", imageOffset, symbolName);
526 const bool reExport
= (flags
& EXPORT_SYMBOL_FLAGS_REEXPORT
);
527 const bool weakDef
= (flags
& EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION
);
528 const bool resolver
= (flags
& EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
);
529 const bool threadLocal
= ((flags
& EXPORT_SYMBOL_FLAGS_KIND_MASK
) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL
);
530 const bool abs
= ((flags
& EXPORT_SYMBOL_FLAGS_KIND_MASK
) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
);
532 printf(" [re-export] ");
534 printf(" 0x%08llX ", imageOffset
);
535 printf("%s", symbolName
);
536 if ( weakDef
|| threadLocal
|| resolver
|| abs
) {
537 bool needComma
= false;
546 printf("per-thread");
558 printf("resolver=0x%08llX", other
);
564 if ( importName
[0] == '\0' )
565 printf(" (from %s)", ordinalName(ma
, (int)other
));
567 printf(" (%s from %s)", importName
, ordinalName(ma
, (int)other
));
574 static void printObjC(const dyld3::MachOAnalyzer
* ma
)
577 const bool contentRebased
= false;
578 const uint32_t pointerSize
= ma
->pointerSize();
580 auto printMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) {
581 const char* type
= "method";
582 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult
;
583 const char* methodName
= ma
->getPrintableString(method
.nameVMAddr
, methodNameResult
);
584 switch (methodNameResult
) {
585 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
:
586 // The string is already valid
588 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted
:
589 methodName
= "### fairplay encrypted";
591 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection
:
592 methodName
= "### protected section";
594 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection
:
595 methodName
= "### unknown section";
598 printf(" %10s 0x%08llX %s\n",
599 type
, methodVMAddr
, methodName
);
603 printf(" type vmaddr data-vmaddr name\n");
604 auto printClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
605 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
606 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
607 const char* type
= "class";
610 dyld3::MachOAnalyzer::PrintableStringResult classNameResult
;
611 const char* className
= ma
->getPrintableString(objcClass
.nameVMAddr(pointerSize
), classNameResult
);
612 switch (classNameResult
) {
613 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
:
614 // The string is already valid
616 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted
:
617 className
= "### fairplay encrypted";
619 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection
:
620 className
= "### protected section";
622 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection
:
623 className
= "### unknown section";
626 printf(" %10s 0x%08llX 0x%08llX %s\n",
627 type
, classVMAddr
, objcClass
.dataVMAddr
, className
);
629 // Now print the methods on this class
630 ma
->forEachObjCMethod(objcClass
.baseMethodsVMAddr(pointerSize
), contentRebased
,
633 auto printCategory
= ^(Diagnostics
& diag
, uint64_t categoryVMAddr
,
634 const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) {
635 const char* type
= "category";
636 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult
;
637 const char* categoryName
= ma
->getPrintableString(objcCategory
.nameVMAddr
, categoryNameResult
);
638 switch (categoryNameResult
) {
639 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
:
640 // The string is already valid
642 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted
:
643 categoryName
= "### fairplay encrypted";
645 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection
:
646 categoryName
= "### protected section";
648 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection
:
649 categoryName
= "### unknown section";
652 printf(" %10s 0x%08llX %s\n",
653 type
, categoryVMAddr
, categoryName
);
655 // Now print the methods on this category
656 ma
->forEachObjCMethod(objcCategory
.instanceMethodsVMAddr
, contentRebased
,
658 ma
->forEachObjCMethod(objcCategory
.classMethodsVMAddr
, contentRebased
,
661 auto printProtocol
= ^(Diagnostics
& diag
, uint64_t protocolVMAddr
,
662 const dyld3::MachOAnalyzer::ObjCProtocol
& objCProtocol
) {
663 const char* type
= "protocol";
664 dyld3::MachOAnalyzer::PrintableStringResult protocolNameResult
;
665 const char* protocolName
= ma
->getPrintableString(objCProtocol
.nameVMAddr
, protocolNameResult
);
666 switch (protocolNameResult
) {
667 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint
:
668 // The string is already valid
670 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted
:
671 protocolName
= "### fairplay encrypted";
673 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection
:
674 protocolName
= "### protected section";
676 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection
:
677 protocolName
= "### unknown section";
680 printf(" %10s 0x%08llX %s\n",
681 type
, protocolVMAddr
, protocolName
);
683 // Now print the methods on this protocol
684 ma
->forEachObjCMethod(objCProtocol
.instanceMethodsVMAddr
, contentRebased
,
686 ma
->forEachObjCMethod(objCProtocol
.classMethodsVMAddr
, contentRebased
,
688 ma
->forEachObjCMethod(objCProtocol
.optionalInstanceMethodsVMAddr
, contentRebased
,
690 ma
->forEachObjCMethod(objCProtocol
.optionalClassMethodsVMAddr
, contentRebased
,
693 ma
->forEachObjCClass(diag
, contentRebased
, printClass
);
694 ma
->forEachObjCCategory(diag
, contentRebased
, printCategory
);
695 ma
->forEachObjCProtocol(diag
, contentRebased
, printProtocol
);
700 fprintf(stderr
, "Usage: dyldinfo [-arch <arch>]* <options>* <mach-o file>+\n"
701 "\t-segments print segments\n"
702 "\t-dependents print dependent dylibs\n"
703 "\t-fixups print locations dyld will rebase/bind\n"
704 "\t-exports print addresses of all symbols this file exports\n"
705 "\t-objc print objc classes, categories, etc\n"
709 static bool inStringVector(const std::vector
<const char*>& vect
, const char* target
)
711 for (const char* str
: vect
) {
712 if ( strcmp(str
, target
) == 0 )
719 int main(int argc
, const char* argv
[])
726 std::vector
<const char*> files
;
727 std::vector
<const char*> cmdLineArchs
;
728 for (int i
=1; i
< argc
; ++i
) {
729 const char* arg
= argv
[i
];
730 if ( arg
[0] == '-' ) {
731 if ( strcmp(arg
, "-arch") == 0 ) {
733 cmdLineArchs
.push_back(argv
[i
]);
736 fprintf(stderr
, "-arch missing architecture name");
742 files
.push_back(arg
);
745 if ( files
.size() == 0 ) {
750 for (const char* path
: files
) {
752 dyld3::closure::FileSystemPhysical fileSystem
;
753 dyld3::closure::LoadedFileInfo info
;
754 char realerPath
[MAXPATHLEN
];
755 __block
bool printedError
= false;
756 if (!fileSystem
.loadFile(path
, info
, realerPath
, ^(const char* format
, ...) {
757 fprintf(stderr
, "dyldinfo: ");
759 va_start(list
, format
);
760 vfprintf(stderr
, format
, list
);
765 fprintf(stderr
, "dyldinfo: %s: file not found\n", path
);
768 __block
std::vector
<const char*> archesForFile
;
769 __block
dyld3::Platform platform
= dyld3::Platform::unknown
;
770 if ( dyld3::FatFile::isFatFile(info
.fileContent
) ) {
771 const dyld3::FatFile
* ff
= (dyld3::FatFile
*)info
.fileContent
;
772 ff
->forEachSlice(diag
, info
.fileContentLen
, ^(uint32_t sliceCpuType
, uint32_t sliceCpuSubType
, const void* sliceStart
, uint64_t sliceSize
, bool& stop
) {
773 const char* sliceArchName
= dyld3::MachOFile::archName(sliceCpuType
, sliceCpuSubType
);
774 if ( cmdLineArchs
.empty() || inStringVector(cmdLineArchs
, sliceArchName
) ) {
775 archesForFile
.push_back(sliceArchName
);
776 const dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)sliceStart
;
777 mf
->forEachSupportedPlatform(^(dyld3::Platform plat
, uint32_t minOS
, uint32_t sdk
) {
778 if ( platform
== dyld3::Platform::unknown
)
785 const dyld3::MachOFile
* mo
= (dyld3::MachOFile
*)info
.fileContent
;
786 if ( mo
->isMachO(diag
, info
.sliceLen
) ) {
787 archesForFile
.push_back(mo
->archName());
788 mo
->forEachSupportedPlatform(^(dyld3::Platform plat
, uint32_t minOS
, uint32_t sdk
) {
789 if ( platform
== dyld3::Platform::unknown
)
794 fprintf(stderr
, "dyldinfo: %s: %s\n", path
, diag
.errorMessage());
798 if ( archesForFile
.empty() ) {
799 fprintf(stderr
, "dyldinfo: %s: does not contain specified arch(s)\n", path
);
802 char loadedPath
[MAXPATHLEN
];
803 for (const char* sliceArch
: archesForFile
) {
804 info
= dyld3::MachOAnalyzer::load(diag
, fileSystem
, path
, dyld3::GradedArchs::forName(sliceArch
), platform
, loadedPath
);
805 if ( diag
.hasError() ) {
806 fprintf(stderr
, "dyldinfo: %s\n", diag
.errorMessage());
809 const dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)info
.fileContent
;
810 printf("%s [%s]:\n", path
, sliceArch
);
812 bool somethingPrinted
= false;
813 for (int i
=1; i
< argc
; ++i
) {
814 const char* arg
= argv
[i
];
817 if ( strcmp(arg
, "-arch") == 0 ) {
818 // handled previously
821 else if ( strcmp(arg
, "-platform") == 0 ) {
823 somethingPrinted
= true;
825 else if ( strcmp(arg
, "-segments") == 0 ) {
827 somethingPrinted
= true;
829 else if ( strcmp(arg
, "-dependents") == 0 ) {
831 somethingPrinted
= true;
833 else if ( strcmp(arg
, "-fixups") == 0 ) {
835 somethingPrinted
= true;
837 else if ( strcmp(arg
, "-exports") == 0 ) {
839 somethingPrinted
= true;
841 else if ( strcmp(arg
, "-fixup_chains") == 0 ) {
843 somethingPrinted
= true;
845 else if ( strcmp(arg
, "-opcodes") == 0 ) {
847 else if ( strcmp(arg
, "-shared_region") == 0 ) {
849 else if ( strcmp(arg
, "-function_starts") == 0 ) {
851 else if ( strcmp(arg
, "-data_in_code") == 0 ) {
853 else if ( strcmp(arg
, "-objc") == 0 ) {
855 somethingPrinted
= true;
858 fprintf(stderr
, "unknown option: %s\n", arg
);
862 if ( !somethingPrinted
) {