]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/dyldinfo.cpp
dyld-832.7.1.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / dyldinfo.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2018-2019 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <mach-o/dyld_priv.h>
34
35 #include <vector>
36 #include <tuple>
37 #include <set>
38 #include <unordered_set>
39 #include <string>
40
41 #include "Array.h"
42 #include "MachOFile.h"
43 #include "MachOLoaded.h"
44 #include "MachOAnalyzer.h"
45 #include "MachOAnalyzerSet.h"
46 #include "ClosureFileSystemPhysical.h"
47 #include "DyldSharedCache.h"
48
49 typedef dyld3::MachOLoaded::ChainedFixupPointerOnDisk ChainedFixupPointerOnDisk;
50
51 static void versionToString(uint32_t value, char buffer[32])
52 {
53 if ( value == 0 )
54 strcpy(buffer, "n/a");
55 else if ( value & 0xFF )
56 sprintf(buffer, "%d.%d.%d", value >> 16, (value >> 8) & 0xFF, value & 0xFF);
57 else
58 sprintf(buffer, "%d.%d", value >> 16, (value >> 8) & 0xFF);
59 }
60
61 static void printPlatforms(const dyld3::MachOAnalyzer* ma)
62 {
63 printf(" -platform:\n");
64 printf(" platform minOS sdk\n");
65 ma->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
66 char osVers[32];
67 char sdkVers[32];
68 versionToString(minOS, osVers);
69 versionToString(sdk, sdkVers);
70 printf(" %15s %-7s %-7s\n", dyld3::MachOFile::platformName(platform), osVers, sdkVers);
71 });
72 }
73
74 static void permString(uint32_t permFlags, char str[4])
75 {
76 str[0] = (permFlags & VM_PROT_READ) ? 'r' : '.';
77 str[1] = (permFlags & VM_PROT_WRITE) ? 'w' : '.';
78 str[2] = (permFlags & VM_PROT_EXECUTE) ? 'x' : '.';
79 str[3] = '\0';
80 }
81
82 static void printSegments(const dyld3::MachOAnalyzer* ma)
83 {
84 if ( ma->inDyldCache() ) {
85 printf(" -segments:\n");
86 printf(" load-address segment section sect-size seg-size perm\n");
87 __block const char* lastSegName = "";
88 ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
89 if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) {
90 char permChars[8];
91 permString(sectInfo.segInfo.protections, permChars);
92 printf(" 0x%08llX %-16s %16lluKB %s\n", sectInfo.segInfo.vmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, permChars);
93 lastSegName = sectInfo.segInfo.segName;
94 }
95 printf(" 0x%08llX %-16s %6llu\n", sectInfo.sectAddr, sectInfo.sectName, sectInfo.sectSize);
96 });
97 }
98 else {
99 printf(" -segments:\n");
100 printf(" load-offset segment section sect-size seg-size perm\n");
101 __block const char* lastSegName = "";
102 __block uint64_t firstSegVmAddr = 0;
103 ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
104 if ( lastSegName[0] == '\0' )
105 firstSegVmAddr = sectInfo.segInfo.vmAddr;
106 if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) {
107 char permChars[8];
108 permString(sectInfo.segInfo.protections, permChars);
109 printf(" 0x%08llX %-16s %6lluKB %s\n", sectInfo.segInfo.vmAddr - firstSegVmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, permChars);
110 lastSegName = sectInfo.segInfo.segName;
111 }
112 printf(" 0x%08llX %-16s %6llu\n", sectInfo.sectAddr-firstSegVmAddr, sectInfo.sectName, sectInfo.sectSize);
113 });
114 }
115 }
116
117
118 static void printDependents(const dyld3::MachOAnalyzer* ma)
119 {
120 printf(" -dependents:\n");
121 printf(" attributes load path\n");
122 ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
123 const char* attribute = "";
124 if ( isWeak )
125 attribute = "weak_import";
126 else if ( isReExport )
127 attribute = "re-export";
128 else if ( isUpward )
129 attribute = "upward";
130 printf(" %-12s %s\n", attribute, loadPath);
131 });
132 }
133
134 static bool liveMachO(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen)
135 {
136 if ( dyldCache == nullptr )
137 return false;
138 const uint8_t* cacheStart = (uint8_t*)dyldCache;
139 const uint8_t* cacheEnd = &cacheStart[cacheLen];
140 if ( (uint8_t*)ma < cacheStart)
141 return false;
142 if ( (uint8_t*)ma > cacheEnd)
143 return false;
144
145 // only return true for live images
146 return ( dyld_image_header_containing_address(ma) != nullptr );
147 }
148
149 static void printInitializers(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen)
150 {
151 printf(" -inits:\n");
152 Diagnostics diag;
153 const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = (ma->inDyldCache() ? dyldCache->makeVMAddrConverter(true) : ma->makeVMAddrConverter(false));
154 ma->forEachInitializer(diag, vmAddrConverter, ^(uint32_t offset) {
155 uint64_t targetLoadAddr = (uint64_t)ma+offset;
156 const char* symbolName;
157 uint64_t symbolLoadAddr;
158 if ( ma->findClosestSymbol(targetLoadAddr, &symbolName, &symbolLoadAddr) ) {
159 uint64_t delta = targetLoadAddr - symbolLoadAddr;
160 if ( delta == 0 )
161 printf(" 0x%08X %s\n", offset, symbolName);
162 else
163 printf(" 0x%08X %s + 0x%llX\n", offset, symbolName, delta);
164 }
165 else
166 printf(" 0x%08X\n", offset);
167 });
168 if ( ma->hasPlusLoadMethod(diag) ) {
169 // can't inspect ObjC of a live dylib
170 if ( liveMachO(ma, dyldCache, cacheLen) ) {
171 printf(" <<<cannot print objc data on live dylib>>>\n");
172 return;
173 }
174 const uint32_t pointerSize = ma->pointerSize();
175 uint64_t prefLoadAddress = ma->preferredLoadAddress();
176 // print all +load methods on classes in this image
177 ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr,
178 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
179 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
180 if (!isMetaClass)
181 return;
182 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
183 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
184 if ( classNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
185 ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
186 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
187 const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
188 if ( methodNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
189 if ( strcmp(methodName, "load") == 0 )
190 printf(" 0x%08llX +[%s %s]\n", methodVMAddr-prefLoadAddress, className, methodName);
191 }
192 });
193 }
194 });
195 // print all +load methods on categories in this image
196 ma->forEachObjCCategory(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t categoryVMAddr,
197 const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
198 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
199 const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
200 if ( categoryNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
201 ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
202 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
203 const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
204 if ( methodNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
205 if ( strcmp(methodName, "load") == 0 ) {
206 // FIXME: if category is on class in another image, forEachObjCCategory returns null for objcCategory.clsVMAddr, need way to get name
207 __block const char* catOnClassName = "";
208 ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr,
209 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
210 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
211 if ( objcCategory.clsVMAddr == classVMAddr ) {
212 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
213 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
214 if ( classNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
215 catOnClassName = className;
216 }
217 }
218 });
219 printf(" 0x%08llX +[%s(%s) %s]\n", methodVMAddr-prefLoadAddress, catOnClassName, categoryName, methodName);
220 }
221 }
222 });
223 }
224 });
225 }
226 }
227
228
229 static const char* pointerFormat(uint16_t format)
230 {
231 switch (format) {
232 case DYLD_CHAINED_PTR_ARM64E:
233 return "authenticated arm64e, 8-byte stride, target vmadddr";
234 case DYLD_CHAINED_PTR_ARM64E_USERLAND:
235 return "authenticated arm64e, 8-byte stride, target vmoffset";
236 case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
237 return "authenticated arm64e, 4-byte stride, target vmadddr";
238 case DYLD_CHAINED_PTR_ARM64E_KERNEL:
239 return "authenticated arm64e, 4-byte stride, target vmoffset";
240 case DYLD_CHAINED_PTR_64:
241 return "generic 64-bit, 4-byte stride, target vmadddr";
242 case DYLD_CHAINED_PTR_64_OFFSET:
243 return "generic 64-bit, 4-byte stride, target vmoffset ";
244 case DYLD_CHAINED_PTR_32:
245 return "generic 32-bit";
246 case DYLD_CHAINED_PTR_32_CACHE:
247 return "32-bit for dyld cache";
248 case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
249 return "64-bit for kernel cache";
250 case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
251 return "64-bit for x86_64 kernel cache";
252 case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
253 return "authenticated arm64e, 8-byte stride, target vmoffset, 24-bit bind ordinals";
254 }
255 return "unknown";
256 }
257
258 static void printChains(const dyld3::MachOAnalyzer* ma)
259 {
260 Diagnostics diag;
261 ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
262 for (int i=0; i < starts->seg_count; ++i) {
263 if ( starts->seg_info_offset[i] == 0 )
264 continue;
265 const dyld_chained_starts_in_segment* seg = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[i]);
266 if ( seg->page_count == 0 )
267 continue;
268 printf("seg[%d]:\n", i);
269 printf(" page_size: 0x%04X\n", seg->page_size);
270 printf(" pointer_format: %d (%s)\n", seg->pointer_format, pointerFormat(seg->pointer_format));
271 printf(" segment_offset: 0x%08llX\n", seg->segment_offset);
272 printf(" max_pointer: 0x%08X\n", seg->max_valid_pointer);
273 printf(" pages: %d\n", seg->page_count);
274 for (int pageIndex=0; pageIndex < seg->page_count; ++pageIndex) {
275 uint16_t offsetInPage = seg->page_start[pageIndex];
276 if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE )
277 continue;
278 if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) {
279 // 32-bit chains which may need multiple starts per page
280 uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI;
281 bool chainEnd = false;
282 while (!chainEnd) {
283 chainEnd = (seg->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST);
284 offsetInPage = (seg->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
285 printf(" start[% 2d]: 0x%04X\n", pageIndex, offsetInPage);
286 ++overflowIndex;
287 }
288 }
289 else {
290 // one chain per page
291 printf(" start[% 2d]: 0x%04X\n", pageIndex, offsetInPage);
292 }
293 }
294 }
295 });
296 }
297
298
299 static void printChainDetails(const dyld3::MachOAnalyzer* ma)
300 {
301 __block Diagnostics diag;
302 ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
303 ma->forEachFixupInAllChains(diag, starts, true, ^(ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
304 uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
305 switch (segInfo->pointer_format) {
306 case DYLD_CHAINED_PTR_ARM64E:
307 case DYLD_CHAINED_PTR_ARM64E_KERNEL:
308 case DYLD_CHAINED_PTR_ARM64E_USERLAND:
309 case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
310 case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
311 if ( fixupLoc->arm64e.authRebase.auth ) {
312 uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.authBind24.ordinal : fixupLoc->arm64e.authBind.ordinal;
313 if ( fixupLoc->arm64e.authBind.bind ) {
314 printf(" 0x%08llX: raw: 0x%016llX auth-bind: (next: %03d, key: %s, addrDiv: %d, diversity: 0x%04X, ordinal: %04X)\n", vmOffset, fixupLoc->raw64,
315 fixupLoc->arm64e.authBind.next, fixupLoc->arm64e.keyName(),
316 fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, bindOrdinal);
317 }
318 else {
319 printf(" 0x%08llX: raw: 0x%016llX auth-rebase: (next: %03d, key: %s, addrDiv: %d, diversity: 0x%04X, target: 0x%08X)\n", vmOffset, fixupLoc->raw64,
320 fixupLoc->arm64e.authRebase.next, fixupLoc->arm64e.keyName(),
321 fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authRebase.target);
322 }
323 }
324 else {
325 uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.bind24.ordinal : fixupLoc->arm64e.bind.ordinal;
326 if ( fixupLoc->arm64e.rebase.bind ) {
327 printf(" 0x%08llX: raw: 0x%016llX bind: (next: %03d, ordinal: %04X, addend: %d)\n", vmOffset, fixupLoc->raw64,
328 fixupLoc->arm64e.bind.next, bindOrdinal, fixupLoc->arm64e.bind.addend);
329 }
330 else {
331 printf(" 0x%08llX: raw: 0x%016llX rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64,
332 fixupLoc->arm64e.rebase.next, fixupLoc->arm64e.rebase.target, fixupLoc->arm64e.rebase.high8);
333 }
334 }
335 break;
336 case DYLD_CHAINED_PTR_64:
337 case DYLD_CHAINED_PTR_64_OFFSET:
338 if ( fixupLoc->generic64.rebase.bind ) {
339 printf(" 0x%08llX: raw: 0x%016llX bind: (next: %03d, ordinal: %06X, addend: %d)\n", vmOffset, fixupLoc->raw64,
340 fixupLoc->generic64.bind.next, fixupLoc->generic64.bind.ordinal, fixupLoc->generic64.bind.addend);
341 }
342 else {
343 printf(" 0x%08llX: raw: 0x%016llX rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64,
344 fixupLoc->generic64.rebase.next, fixupLoc->generic64.rebase.target, fixupLoc->generic64.rebase.high8);
345 }
346 break;
347 case DYLD_CHAINED_PTR_32:
348 if ( fixupLoc->generic32.bind.bind ) {
349 printf(" 0x%08llX: raw: 0x%08X bind: (next:%02d ordinal:%05X addend:%d)\n", vmOffset, fixupLoc->raw32,
350 fixupLoc->generic32.bind.next, fixupLoc->generic32.bind.ordinal, fixupLoc->generic32.bind.addend);
351 }
352 else if ( fixupLoc->generic32.rebase.target > segInfo->max_valid_pointer ) {
353 uint32_t bias = (0x04000000 + segInfo->max_valid_pointer)/2;
354 uint32_t value = fixupLoc->generic32.rebase.target - bias;
355 printf(" 0x%08llX: raw: 0x%08X nonptr: (next:%02d value: 0x%08X)\n", vmOffset, fixupLoc->raw32,
356 fixupLoc->generic32.rebase.next, value);
357 }
358 else {
359 printf(" 0x%08llX: raw: 0x%08X rebase: (next:%02d target: 0x%07X)\n", vmOffset, fixupLoc->raw32,
360 fixupLoc->generic32.rebase.next, fixupLoc->generic32.rebase.target);
361 }
362 break;
363 default:
364 fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
365 break;
366 }
367 });
368 });
369 if ( diag.hasError() )
370 fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
371 }
372
373
374 struct FixupInfo
375 {
376 std::string segName;
377 std::string sectName;
378 uint64_t address;
379 dyld3::MachOAnalyzerSet::PointerMetaData pmd;
380 const char* type;
381 uint64_t targetValue;
382 const char* targetDylib;
383 const char* targetSymbolName;
384 uint64_t targetAddend;
385 bool targetWeakImport;
386 };
387
388
389 struct SymbolicFixupInfo
390 {
391 uint64_t address;
392 const char* kind;
393 std::string target;
394 };
395
396
397
398 static const char* ordinalName(const dyld3::MachOAnalyzer* ma, int libraryOrdinal)
399 {
400 static int sLastOrdinal = -100;
401 static const char* sLastString = nullptr;
402 if ( libraryOrdinal > 0 ) {
403 if ( libraryOrdinal != sLastOrdinal ) {
404 const char* path = ma->dependentDylibLoadPath(libraryOrdinal-1);
405 if ( path == nullptr )
406 return "ordinal-too-large";
407 const char* leafName = path;
408 if ( const char* lastSlash = strrchr(path, '/') )
409 leafName = lastSlash+1;
410 sLastOrdinal = libraryOrdinal;
411 sLastString = leafName;
412 }
413 return sLastString;
414 }
415 else {
416 switch ( libraryOrdinal) {
417 case BIND_SPECIAL_DYLIB_SELF:
418 return "this-image";
419 case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
420 return "main-executable";
421 case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
422 return "flat-namespace";
423 case BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
424 return "weak-coalesce";
425 }
426 }
427 return "unknown-ordinal";
428 }
429
430
431 class SectionFinder
432 {
433 public:
434 SectionFinder(const dyld3::MachOAnalyzer* ma);
435 const char* segmentName(uint64_t vmOffset) const;
436 const char* sectionName(uint64_t vmOffset) const;
437 uint64_t baseAddress() const { return _baseAddress; }
438 uint64_t currentSectionAddress() const { return _lastSection.sectAddr; }
439 bool isNewSection(uint64_t vmOffset) const;
440
441 private:
442 void updateLastSection(uint64_t vmOffset) const;
443
444 const dyld3::MachOAnalyzer* _ma;
445 uint64_t _baseAddress;
446 mutable dyld3::MachOFile::SectionInfo _lastSection;
447 mutable char _lastSegName[20];
448 mutable char _lastSectName[20];
449 };
450
451 SectionFinder::SectionFinder(const dyld3::MachOAnalyzer* ma)
452 : _ma(ma)
453 {
454 _baseAddress = ma->preferredLoadAddress();
455 _lastSection.sectAddr = 0;
456 _lastSection.sectSize = 0;
457 }
458
459 bool SectionFinder::isNewSection(uint64_t vmOffset) const
460 {
461 uint64_t vmAddr = _baseAddress + vmOffset;
462 return ( (vmAddr < _lastSection.sectAddr) || (vmAddr >= _lastSection.sectAddr+_lastSection.sectSize) );
463 }
464
465 void SectionFinder::updateLastSection(uint64_t vmOffset) const
466 {
467 if ( isNewSection(vmOffset) ) {
468 _lastSegName[0] = '\0';
469 _lastSectName[0] = '\0';
470 uint64_t vmAddr = _baseAddress + vmOffset;
471 _ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& sectStop) {
472 if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) {
473 _lastSection = sectInfo;
474 strcpy(_lastSegName, _lastSection.segInfo.segName);
475 strcpy(_lastSectName, _lastSection.sectName);
476 sectStop = true;
477 }
478 });
479 }
480 }
481
482 const char* SectionFinder::segmentName(uint64_t vmOffset) const
483 {
484 updateLastSection(vmOffset);
485 return _lastSegName;
486 }
487
488 const char* SectionFinder::sectionName(uint64_t vmOffset) const
489 {
490 updateLastSection(vmOffset);
491 return _lastSectName;
492 }
493
494
495 static inline std::string decimal(int64_t value) {
496 char buff[64];
497 sprintf(buff, "%lld", value);
498 return buff;
499 }
500
501 static inline std::string hex(int64_t value) {
502 char buff[64];
503 sprintf(buff, "0x%llX", value);
504 return buff;
505 }
506
507 static std::string rebaseTargetString(const dyld3::MachOAnalyzer* ma, uint64_t vmAddr)
508 {
509 uint64_t targetLoadAddr = (uint64_t)ma+vmAddr;
510 const char* targetSymbolName;
511 uint64_t targetSymbolLoadAddr;
512 if ( ma->findClosestSymbol(targetLoadAddr, &targetSymbolName, &targetSymbolLoadAddr) ) {
513 uint64_t delta = targetLoadAddr - targetSymbolLoadAddr;
514 if ( delta == 0 ) {
515 return targetSymbolName;
516 }
517 else {
518 if ( (delta == 1) && (ma->cputype == CPU_TYPE_ARM) )
519 return std::string(targetSymbolName) + std::string(" [thumb]");
520 else
521 return std::string(targetSymbolName) + std::string("+") + decimal(delta);
522 }
523 }
524 else {
525 __block std::string result;
526 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
527 if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) {
528 if ( (sectInfo.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) {
529 const char* cstring = (char*)ma + (vmAddr-ma->preferredLoadAddress());
530 result = std::string("\"") + cstring + std::string("\"");
531 }
532 else {
533 result = std::string(sectInfo.segInfo.segName) + "/" + sectInfo.sectName + "+" + decimal(vmAddr - sectInfo.sectAddr);
534 }
535 }
536 });
537 return result;
538 }
539 }
540
541
542
543 typedef dyld3::MachOAnalyzerSet MachOAnalyzerSet;
544 typedef dyld3::MachOAnalyzerSet::WrappedMachO WrappedMachO;
545
546 struct InfoAnalyzerSet : public MachOAnalyzerSet
547 {
548 void mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const override;
549 void mas_mainExecutable(WrappedMachO& anImage) const override;
550 void* mas_dyldCache() const override;
551 bool wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const override;
552 const char* wmo_path(const WrappedMachO* image) const override;
553 ExportsTrie wmo_getExportsTrie(const WrappedMachO* image) const override;
554 bool wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport,
555 bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const override;
556 bool wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const override;
557 };
558
559 bool InfoAnalyzerSet::wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const
560 {
561 const char* depPath = image->_mh->dependentDylibLoadPath(depIndex);
562 childObj = WrappedMachO(nullptr, image->_set, (void*)depPath);
563 return true;
564 }
565
566 const char* InfoAnalyzerSet::wmo_path(const WrappedMachO* image) const
567 {
568 return (const char*)image->_other;
569 }
570
571 MachOAnalyzerSet::ExportsTrie InfoAnalyzerSet::wmo_getExportsTrie(const WrappedMachO* obj) const
572 {
573 return { nullptr, nullptr };
574 }
575
576 bool InfoAnalyzerSet::wmo_findSymbolFrom(const WrappedMachO* obj, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport,
577 bool lazyBind, uint64_t addend, CachePatchHandler ph, MachOAnalyzerSet::FixupTarget& target) const
578 {
579 target.requestedSymbolName = symbolName;
580 target.foundSymbolName = symbolName;
581 target.addend = addend;
582 target.kind = weakImport ? MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol : MachOAnalyzerSet::FixupTarget::Kind::bindToImage;
583 if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
584 target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"self");
585 }
586 else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
587 target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"main-executable");
588 }
589 else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
590 target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"flat-namespace");
591 }
592 else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
593 target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"weak-coalesce");
594 }
595 else {
596 int depIndex = libOrdinal - 1;
597 const char* depPath = obj->_mh->dependentDylibLoadPath(depIndex);
598 const char* lastSlash = strrchr(depPath, '/');
599 if (lastSlash)
600 ++lastSlash;
601 else
602 lastSlash = depPath;
603 target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)lastSlash);
604 }
605 return true;
606 }
607
608 void InfoAnalyzerSet::mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const
609 {
610 WrappedMachO anImage(nullptr, this, (void*)"flat-namespace");
611 bool stop = false;
612 handler(anImage, false, stop);
613 }
614
615
616 bool InfoAnalyzerSet::wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const
617 {
618 return false;
619 }
620
621 void InfoAnalyzerSet::mas_mainExecutable(WrappedMachO& anImage) const
622 {
623 anImage = WrappedMachO(nullptr, this, (void*)"main-executable");
624 }
625
626 void* InfoAnalyzerSet::mas_dyldCache() const
627 {
628 return nullptr;
629 }
630
631 static void printFixups(const dyld3::MachOAnalyzer* ma, const char* path)
632 {
633 printf(" -fixups:\n");
634 Diagnostics diag;
635 __block std::vector<FixupInfo> fixups;
636 uint64_t prefLoadAddr = ma->preferredLoadAddress();
637 SectionFinder namer(ma);
638 InfoAnalyzerSet ias;
639 WrappedMachO wm(ma, &ias, (void*)path);
640 wm.forEachFixup(diag, ^(uint64_t fixupLocRuntimeOffset, MachOAnalyzerSet::PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target, bool& stop) {
641 FixupInfo fixup;
642 fixup.segName = namer.segmentName(fixupLocRuntimeOffset);
643 fixup.sectName = namer.sectionName(fixupLocRuntimeOffset);
644 fixup.address = prefLoadAddr + fixupLocRuntimeOffset;
645 fixup.pmd = pmd;
646 fixup.targetWeakImport = false;
647 switch ( target.kind ) {
648 case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute:
649 fixup.type = "absolute";
650 break;
651 case MachOAnalyzerSet::FixupTarget::Kind::rebase:
652 fixup.type = "rebase";
653 fixup.targetSymbolName = nullptr;
654 fixup.targetDylib = nullptr;
655 break;
656 case MachOAnalyzerSet::FixupTarget::Kind::bindToImage:
657 fixup.type = "bind";
658 fixup.targetSymbolName = target.requestedSymbolName;
659 fixup.targetDylib = target.foundInImage.path();
660 break;
661 case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol:
662 fixup.type = "bind";
663 fixup.targetSymbolName = target.requestedSymbolName;
664 fixup.targetDylib = target.foundInImage.path();
665 fixup.targetWeakImport = true;
666 break;
667 }
668 fixup.targetValue = target.offsetInImage;
669 fixup.targetAddend = target.addend;
670 if ( pmd.high8 )
671 fixup.targetAddend += ((uint64_t)pmd.high8 << 56);
672 fixups.push_back(fixup);
673 },
674 ^(uint32_t, uint32_t, const MachOAnalyzerSet::FixupTarget&) {
675 });
676
677 std::sort(fixups.begin(), fixups.end(), [](const FixupInfo& l, const FixupInfo& r) {
678 if ( &l == &r )
679 return false;
680 if ( l.address == r.address )
681 return (l.targetSymbolName == nullptr);
682 return ( l.address < r.address );
683 });
684
685 printf(" segment section address type target\n");
686 for (const FixupInfo& fixup : fixups) {
687 char authInfo[128];
688 authInfo[0] = '\0';
689 if ( fixup.pmd.authenticated ) {
690 sprintf(authInfo, " (div=0x%04X ad=%d key=%s)", fixup.pmd.diversity, fixup.pmd.usesAddrDiversity, ChainedFixupPointerOnDisk::Arm64e::keyName(fixup.pmd.key));
691 }
692 if ( fixup.targetSymbolName == nullptr )
693 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetValue, authInfo);
694 else if ( fixup.targetAddend != 0 )
695 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, fixup.targetAddend, authInfo);
696 else if ( fixup.targetWeakImport )
697 printf(" %-12s %-16s 0x%08llX %16s %s/%s [weak-import]%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, authInfo);
698 else
699 printf(" %-12s %-16s 0x%08llX %16s %s/%s%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, authInfo);
700 }
701 }
702
703 static void printSymbolicFixups(const dyld3::MachOAnalyzer* ma, const char* path)
704 {
705 printf(" -symbolic_fixups:\n");
706 Diagnostics diag;
707 __block std::vector<SymbolicFixupInfo> fixups;
708 uint64_t prefLoadAddr = ma->preferredLoadAddress();
709 InfoAnalyzerSet ias;
710 WrappedMachO wm(ma, &ias, (void*)path);
711 wm.forEachFixup(diag, ^(uint64_t fixupLocRuntimeOffset, MachOAnalyzerSet::PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target, bool& stop) {
712 SymbolicFixupInfo fixup;
713 fixup.address = prefLoadAddr + fixupLocRuntimeOffset;
714 switch ( target.kind ) {
715 case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute:
716 fixup.kind = "absolute";
717 fixup.target = "";
718 break;
719 case MachOAnalyzerSet::FixupTarget::Kind::rebase:
720 fixup.kind = "rebase pointer";
721 fixup.target = rebaseTargetString(ma, target.offsetInImage);
722 break;
723 case MachOAnalyzerSet::FixupTarget::Kind::bindToImage:
724 case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol:
725 fixup.kind = "bind pointer";
726 fixup.target = std::string(target.foundInImage.path()) + "/" + target.requestedSymbolName;
727 if ( target.addend != 0 )
728 fixup.target += std::string("+") + decimal(target.addend);
729 if ( pmd.high8 )
730 fixup.target += std::string("+") + hex(((uint64_t)pmd.high8 << 56));
731 //if ( target.weakImport )
732 // fixup.target += " [weak-import]";
733 break;
734 }
735 if ( pmd.authenticated ) {
736 char authInfo[256];
737 sprintf(authInfo, " (div=0x%04X ad=%d key=%s)", pmd.diversity, pmd.usesAddrDiversity, ChainedFixupPointerOnDisk::Arm64e::keyName(pmd.key));
738 fixup.target += authInfo;
739 }
740 fixups.push_back(fixup);
741 },
742 ^(uint32_t, uint32_t, const MachOAnalyzerSet::FixupTarget&) {
743 });
744
745
746 std::sort(fixups.begin(), fixups.end(), [](const SymbolicFixupInfo& l, const SymbolicFixupInfo& r) {
747 if ( &l == &r )
748 return false;
749 return ( l.address < r.address );
750 });
751
752 SectionFinder sectionTracker(ma);
753 uint64_t lastSymbolVmOffset = 0;
754 for (const SymbolicFixupInfo& fixup : fixups) {
755 uint64_t vmAddr = fixup.address;
756 uint64_t vmOffset = vmAddr - prefLoadAddr;
757 if ( sectionTracker.isNewSection(vmOffset) ) {
758 printf(" 0x%08llX %-12s %-16s \n", vmAddr, sectionTracker.segmentName(vmOffset), sectionTracker.sectionName(vmOffset));
759 }
760 const char* symbolName;
761 uint64_t symbolLoadAddr = 0;
762 if ( ma->findClosestSymbol((uint64_t)ma+vmOffset, &symbolName, &symbolLoadAddr) ) {
763 uint64_t symbolVmOffset = symbolLoadAddr - (uint64_t)ma;
764 if ( symbolVmOffset != lastSymbolVmOffset ) {
765 printf(" %s:\n", symbolName);
766 lastSymbolVmOffset = symbolVmOffset;
767 }
768 }
769 printf(" +0x%04llX %16s %s\n", vmOffset - lastSymbolVmOffset, fixup.kind, fixup.target.c_str());
770 }
771 }
772
773
774 static void printExports(const dyld3::MachOAnalyzer* ma)
775 {
776 printf(" -exports:\n");
777 printf(" offset symbol\n");
778 Diagnostics diag;
779 ma->forEachExportedSymbol(diag, ^(const char* symbolName, uint64_t imageOffset, uint64_t flags, uint64_t other, const char* importName, bool& stop) {
780 //printf("0x%08llX %s\n", imageOffset, symbolName);
781 const bool reExport = (flags & EXPORT_SYMBOL_FLAGS_REEXPORT);
782 const bool weakDef = (flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
783 const bool resolver = (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
784 const bool threadLocal = ((flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL);
785 const bool abs = ((flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
786 if ( reExport )
787 printf(" [re-export] ");
788 else
789 printf(" 0x%08llX ", imageOffset);
790 printf("%s", symbolName);
791 if ( weakDef || threadLocal || resolver || abs ) {
792 bool needComma = false;
793 printf(" [");
794 if ( weakDef ) {
795 printf("weak_def");
796 needComma = true;
797 }
798 if ( threadLocal ) {
799 if ( needComma )
800 printf(", ");
801 printf("per-thread");
802 needComma = true;
803 }
804 if ( abs ) {
805 if ( needComma )
806 printf(", ");
807 printf("absolute");
808 needComma = true;
809 }
810 if ( resolver ) {
811 if ( needComma )
812 printf(", ");
813 printf("resolver=0x%08llX", other);
814 needComma = true;
815 }
816 printf("]");
817 }
818 if ( reExport ) {
819 if ( importName[0] == '\0' )
820 printf(" (from %s)", ordinalName(ma, (int)other));
821 else
822 printf(" (%s from %s)", importName, ordinalName(ma, (int)other));
823 }
824 printf("\n");
825 });
826
827 }
828
829 static void printObjC(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen)
830 {
831 Diagnostics diag;
832 const uint32_t pointerSize = ma->pointerSize();
833 const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = (ma->inDyldCache() ? dyldCache->makeVMAddrConverter(true) : ma->makeVMAddrConverter(false));
834
835 auto printMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
836 const char* type = "method";
837 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
838 const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
839 switch (methodNameResult) {
840 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint:
841 // The string is already valid
842 break;
843 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
844 methodName = "### fairplay encrypted";
845 break;
846 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
847 methodName = "### protected section";
848 break;
849 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
850 methodName = "### unknown section";
851 break;
852 }
853 printf(" %10s 0x%08llX %s\n",
854 type, methodVMAddr, methodName);
855 };
856
857 printf(" -objc:\n");
858 // can't inspect ObjC of a live dylib
859 if ( liveMachO(ma, dyldCache, cacheLen) ) {
860 printf(" <<<cannot print objc data on live dylib>>>\n");
861 return;
862 }
863 printf(" type vmaddr data-vmaddr name\n");
864 auto printClass = ^(Diagnostics& diag, uint64_t classVMAddr,
865 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
866 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
867 const char* type = "class";
868 if (isMetaClass)
869 type = "meta-class";
870 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
871 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
872 switch (classNameResult) {
873 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint:
874 // The string is already valid
875 break;
876 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
877 className = "### fairplay encrypted";
878 break;
879 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
880 className = "### protected section";
881 break;
882 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
883 className = "### unknown section";
884 break;
885 }
886 printf(" %10s 0x%08llX 0x%08llX %s\n",
887 type, classVMAddr, objcClass.dataVMAddr, className);
888
889 // Now print the methods on this class
890 ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, printMethod);
891 };
892 auto printCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
893 const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
894 const char* type = "category";
895 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
896 const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
897 switch (categoryNameResult) {
898 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint:
899 // The string is already valid
900 break;
901 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
902 categoryName = "### fairplay encrypted";
903 break;
904 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
905 categoryName = "### protected section";
906 break;
907 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
908 categoryName = "### unknown section";
909 break;
910 }
911 printf(" %10s 0x%08llX %s\n",
912 type, categoryVMAddr, categoryName);
913
914 // Now print the methods on this category
915 ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter,
916 printMethod);
917 ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter,
918 printMethod);
919 };
920 auto printProtocol = ^(Diagnostics& diag, uint64_t protocolVMAddr,
921 const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol) {
922 const char* type = "protocol";
923 dyld3::MachOAnalyzer::PrintableStringResult protocolNameResult;
924 const char* protocolName = ma->getPrintableString(objCProtocol.nameVMAddr, protocolNameResult);
925 switch (protocolNameResult) {
926 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint:
927 // The string is already valid
928 break;
929 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
930 protocolName = "### fairplay encrypted";
931 break;
932 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
933 protocolName = "### protected section";
934 break;
935 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
936 protocolName = "### unknown section";
937 break;
938 }
939 printf(" %10s 0x%08llX %s\n",
940 type, protocolVMAddr, protocolName);
941
942 // Now print the methods on this protocol
943 ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, vmAddrConverter,
944 printMethod);
945 ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, vmAddrConverter,
946 printMethod);
947 ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, vmAddrConverter,
948 printMethod);
949 ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, vmAddrConverter,
950 printMethod);
951 };
952 ma->forEachObjCClass(diag, vmAddrConverter, printClass);
953 ma->forEachObjCCategory(diag, vmAddrConverter, printCategory);
954 ma->forEachObjCProtocol(diag, vmAddrConverter, printProtocol);
955 }
956
957 static void usage()
958 {
959 fprintf(stderr, "Usage: dyldinfo [-arch <arch>]* <options>* <mach-o file>+\n"
960 "\t-segments print segments\n"
961 "\t-dependents print dependent dylibs\n"
962 "\t-fixups print locations dyld will rebase/bind\n"
963 "\t-exports print addresses of all symbols this file exports\n"
964 "\t-objc print objc classes, categories, etc\n"
965 );
966 }
967
968 static bool inStringVector(const std::vector<const char*>& vect, const char* target)
969 {
970 for (const char* str : vect) {
971 if ( strcmp(str, target) == 0 )
972 return true;
973 }
974 return false;
975 }
976
977
978 int main(int argc, const char* argv[])
979 {
980 if ( argc == 1 ) {
981 usage();
982 return 0;
983 }
984
985 std::vector<const char*> files;
986 std::vector<const char*> cmdLineArchs;
987 for (int i=1; i < argc; ++i) {
988 const char* arg = argv[i];
989 if ( arg[0] == '-' ) {
990 if ( strcmp(arg, "-arch") == 0 ) {
991 if ( ++i < argc ) {
992 cmdLineArchs.push_back(argv[i]);
993 }
994 else {
995 fprintf(stderr, "-arch missing architecture name");
996 return 1;
997 }
998 }
999 }
1000 else {
1001 files.push_back(arg);
1002 }
1003 }
1004 if ( files.size() == 0 ) {
1005 usage();
1006 return 0;
1007 }
1008
1009 size_t cacheLen;
1010 const DyldSharedCache* cache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLen);
1011 for (const char* path : files) {
1012 Diagnostics diag;
1013 bool fromSharedCache = false;
1014 dyld3::closure::FileSystemPhysical fileSystem;
1015 dyld3::closure::LoadedFileInfo info;
1016 __block std::vector<const char*> archesForFile;
1017 char realerPath[MAXPATHLEN];
1018 __block bool printedError = false;
1019 if (!fileSystem.loadFile(path, info, realerPath, ^(const char* format, ...) {
1020 fprintf(stderr, "dyldinfo: ");
1021 va_list list;
1022 va_start(list, format);
1023 vfprintf(stderr, format, list);
1024 va_end(list);
1025 printedError = true;
1026 })) {
1027 if ( printedError )
1028 return 1;
1029 // see if path is in current dyld shared cache
1030 info.fileContent = nullptr;
1031 if ( cache != nullptr ) {
1032 uint32_t imageIndex;
1033 if ( cache->hasImagePath(path, imageIndex) ) {
1034 uint64_t mTime;
1035 uint64_t inode;
1036 const mach_header* mh = cache->getIndexedImageEntry(imageIndex, mTime, inode);
1037 info.fileContent = mh;
1038 info.path = path;
1039 fromSharedCache = true;
1040 archesForFile.push_back(cache->archName());
1041 }
1042 }
1043 if ( !fromSharedCache ) {
1044 fprintf(stderr, "dyldinfo: %s: file not found\n", path);
1045 return 1;
1046 }
1047 }
1048 __block dyld3::Platform platform = dyld3::Platform::unknown;
1049 if ( dyld3::FatFile::isFatFile(info.fileContent) ) {
1050 const dyld3::FatFile* ff = (dyld3::FatFile*)info.fileContent;
1051 ff->forEachSlice(diag, info.fileContentLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
1052 const char* sliceArchName = dyld3::MachOFile::archName(sliceCpuType, sliceCpuSubType);
1053 if ( cmdLineArchs.empty() || inStringVector(cmdLineArchs, sliceArchName) ) {
1054 archesForFile.push_back(sliceArchName);
1055 const dyld3::MachOFile* mf = (dyld3::MachOFile*)sliceStart;
1056 mf->forEachSupportedPlatform(^(dyld3::Platform plat, uint32_t minOS, uint32_t sdk) {
1057 if ( platform == dyld3::Platform::unknown)
1058 platform = plat;
1059 });
1060 }
1061 });
1062 }
1063 else if ( !fromSharedCache ) {
1064 const dyld3::MachOFile* mo = (dyld3::MachOFile*)info.fileContent;
1065 if ( mo->isMachO(diag, info.sliceLen) ) {
1066 archesForFile.push_back(mo->archName());
1067 mo->forEachSupportedPlatform(^(dyld3::Platform plat, uint32_t minOS, uint32_t sdk) {
1068 if ( platform == dyld3::Platform::unknown)
1069 platform = plat;
1070 });
1071 }
1072 else {
1073 fprintf(stderr, "dyldinfo: %s: %s\n", path, diag.errorMessage());
1074 return 1;
1075 }
1076 }
1077 if ( archesForFile.empty() ) {
1078 fprintf(stderr, "dyldinfo: %s: does not contain specified arch(s)\n", path);
1079 return 1;
1080 }
1081 char loadedPath[MAXPATHLEN];
1082 for (const char* sliceArch : archesForFile) {
1083 if ( !fromSharedCache )
1084 info = dyld3::MachOAnalyzer::load(diag, fileSystem, path, dyld3::GradedArchs::forName(sliceArch), platform, loadedPath);
1085 if ( diag.hasError() ) {
1086 fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
1087 return 1;
1088 }
1089 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)info.fileContent;
1090 printf("%s [%s]:\n", path, sliceArch);
1091
1092 bool somethingPrinted = false;
1093 for (int i=1; i < argc; ++i) {
1094 const char* arg = argv[i];
1095 if ( arg[0] != '-' )
1096 continue;
1097 if ( strcmp(arg, "-arch") == 0 ) {
1098 // handled previously
1099 ++i;
1100 }
1101 else if ( strcmp(arg, "-platform") == 0 ) {
1102 printPlatforms(ma);
1103 somethingPrinted = true;
1104 }
1105 else if ( strcmp(arg, "-segments") == 0 ) {
1106 printSegments(ma);
1107 somethingPrinted = true;
1108 }
1109 else if ( strcmp(arg, "-dependents") == 0 ) {
1110 printDependents(ma);
1111 somethingPrinted = true;
1112 }
1113 else if ( strcmp(arg, "-inits") == 0 ) {
1114 printInitializers(ma, cache, cacheLen);
1115 somethingPrinted = true;
1116 }
1117 else if ( strcmp(arg, "-fixups") == 0 ) {
1118 printFixups(ma, path);
1119 somethingPrinted = true;
1120 }
1121 else if ( strcmp(arg, "-exports") == 0 ) {
1122 printExports(ma);
1123 somethingPrinted = true;
1124 }
1125 else if ( strcmp(arg, "-fixup_chains") == 0 ) {
1126 printChains(ma);
1127 somethingPrinted = true;
1128 }
1129 else if ( strcmp(arg, "-fixup_chain_details") == 0 ) {
1130 printChainDetails(ma);
1131 somethingPrinted = true;
1132 }
1133 else if ( strcmp(arg, "-symbolic_fixups") == 0 ) {
1134 printSymbolicFixups(ma, path);
1135 somethingPrinted = true;
1136 }
1137 else if ( strcmp(arg, "-opcodes") == 0 ) {
1138 }
1139 else if ( strcmp(arg, "-shared_region") == 0 ) {
1140 }
1141 else if ( strcmp(arg, "-function_starts") == 0 ) {
1142 }
1143 else if ( strcmp(arg, "-data_in_code") == 0 ) {
1144 }
1145 else if ( strcmp(arg, "-objc") == 0 ) {
1146 printObjC(ma, cache, cacheLen);
1147 somethingPrinted = true;
1148 }
1149 else {
1150 fprintf(stderr, "unknown option: %s\n", arg);
1151 return 1;
1152 }
1153 }
1154 if ( !somethingPrinted ) {
1155 printPlatforms(ma);
1156 printSegments(ma);
1157 printDependents(ma);
1158 printInitializers(ma, cache, cacheLen);
1159 printFixups(ma, path);
1160 printExports(ma);
1161 printObjC(ma, cache, cacheLen);
1162 }
1163
1164 }
1165 }
1166
1167 return 0;
1168 }
1169
1170
1171