]> git.saurik.com Git - apple/dyld.git/blob - dyld3/shared-cache/dyldinfo.cpp
0e1817eec520d37f9600ddb59c4f3ca350cae9f0
[apple/dyld.git] / dyld3 / shared-cache / dyldinfo.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2008-2010 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
34 #include <vector>
35 #include <tuple>
36 #include <set>
37 #include <unordered_set>
38 #include <string>
39
40 #include "Array.h"
41 #include "MachOFile.h"
42 #include "MachOLoaded.h"
43 #include "MachOAnalyzer.h"
44 #include "ClosureFileSystemPhysical.h"
45
46 static bool printSharedRegion = false;
47 static bool printFunctionStarts = false;
48 static bool printDataCode = false;
49
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 printSegments(const dyld3::MachOAnalyzer* ma)
75 {
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;
89 }
90 printf(" 0x%08llX %-16s %6llu\n", sectInfo.sectAddr-firstSegVmAddr, sectInfo.sectName, sectInfo.sectSize);
91
92 });
93
94 }
95
96
97 static void printDependents(const dyld3::MachOAnalyzer* ma)
98 {
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 = "";
103 if ( isWeak )
104 attribute = "weak_import";
105 else if ( isReExport )
106 attribute = "re-export";
107 else if ( isUpward )
108 attribute = "upward";
109 printf(" %-12s %s\n", attribute, loadPath);
110 });
111 }
112
113 static const char* rebaseTypeName(uint8_t type)
114 {
115 switch (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";
122 }
123 return "!!unknown!!";
124 }
125
126 static const char* bindTypeName(uint8_t type)
127 {
128 switch (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";
135 }
136 return "!!unknown!!";
137 }
138
139 static const char* pointerFormat(uint16_t format)
140 {
141 switch (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";
150 }
151 return "unknown";
152 }
153
154 static void printChains(const dyld3::MachOAnalyzer* ma)
155 {
156 Diagnostics diag;
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 )
160 continue;
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 )
163 continue;
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]);
172 }
173 }
174 });
175 }
176
177 struct FixupInfo
178 {
179 const char* segName;
180 std::string sectName;
181 uint64_t address;
182 const char* type;
183 uint64_t targetValue;
184 const char* targetDylib;
185 const char* targetSymbolName;
186 uint64_t targetAddend;
187 bool targetWeakImport;
188 };
189
190
191
192 static const char* ordinalName(const dyld3::MachOAnalyzer* ma, int libraryOrdinal)
193 {
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;
206 }
207 return sLastString;
208 }
209 else {
210 switch ( libraryOrdinal) {
211 case BIND_SPECIAL_DYLIB_SELF:
212 return "this-image";
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";
219 }
220 }
221 return "unknown-ordinal";
222 }
223
224
225 class SectionFinder
226 {
227 public:
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; }
232
233 private:
234 void updateLastSection(uint64_t vmOffset) const;
235
236 const dyld3::MachOAnalyzer* _ma;
237 uint64_t _baseAddress;
238 mutable dyld3::MachOFile::SectionInfo _lastSection;
239 mutable char _lastSectName[20];
240 };
241
242 SectionFinder::SectionFinder(const dyld3::MachOAnalyzer* ma)
243 : _ma(ma)
244 {
245 _baseAddress = ma->preferredLoadAddress();
246 _lastSection.sectAddr = 0;
247 _lastSection.sectSize = 0;
248 }
249
250 void SectionFinder::updateLastSection(uint64_t vmOffset) const
251 {
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);
258 sectStop = true;
259 }
260 });
261 }
262 }
263
264 const char* SectionFinder::segmentName(uint64_t vmOffset) const
265 {
266 updateLastSection(vmOffset);
267 return _lastSection.segInfo.segName;
268 }
269
270 const char* SectionFinder::sectionName(uint64_t vmOffset) const
271 {
272 updateLastSection(vmOffset);
273 return _lastSectName;
274 }
275
276
277
278 static void printPreloadChainedFixups(const dyld3::MachOAnalyzer* ma)
279 {
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]);
289 bool done = false;
290 while (!done) {
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);
296 p += p->next;
297 }
298 }
299 }
300
301 }
302 }
303
304
305
306 struct FixupTarget
307 {
308 uint64_t value;
309 const char* dylib;
310 const char* symbolName;
311 uint64_t addend;
312 bool weakImport;
313 };
314
315
316 static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
317 {
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) {
322 FixupTarget target;
323 target.value = 0;
324 target.dylib = ordinalName(ma, libOrdinal);
325 target.symbolName = symbolName;
326 target.addend = addend;
327 target.weakImport = weakImport;
328 targets.push_back(target);
329 });
330 if ( diag.hasError() )
331 return;
332
333 uint64_t baseAddress = ma->preferredLoadAddress();
334
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);
350 else
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);
355 }
356 else {
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);
362 }
363 }
364 else {
365 if ( fixupLoc->arm64e.rebase.bind ) {
366 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
367 uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
368 if ( fullAddend )
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);
372 else
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);
376 }
377 else {
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);
382 }
383 }
384 break;
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();
389 if ( fullAddend )
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);
393 else
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);
397 }
398 else {
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);
403 }
404 break;
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;
409 if ( fullAddend )
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);
413 else
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);
417 }
418 else {
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);
423 }
424 break;
425 default:
426 fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
427 break;
428 }
429 });
430 });
431 }
432
433 static void printOpcodeFixups(const dyld3::MachOAnalyzer* ma)
434 {
435 Diagnostics diag;
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));
445 FixupInfo fixup;
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);
456 });
457
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();
465 FixupInfo fixup;
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) {
477 },^() { });
478
479
480 std::sort(fixups.begin(), fixups.end(), [](const FixupInfo& l, const FixupInfo& r) {
481 if ( &l == &r )
482 return false;
483 if ( l.address == r.address )
484 return (l.targetSymbolName == nullptr);
485 return ( l.address < r.address );
486 });
487
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);
496 else
497 printf(" %-12s %-16s 0x%08llX %16s %s/%s\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName);
498 }
499
500
501 }
502
503
504 static void printFixups(const dyld3::MachOAnalyzer* ma)
505 {
506 printf(" -fixups:\n");
507 if ( ma->isPreload() || ma->isStaticExecutable() ) {
508 printPreloadChainedFixups(ma);
509 }
510 else if ( ma->hasChainedFixups() ) {
511 printChainedFixups(ma);
512 }
513 else {
514 printOpcodeFixups(ma);
515 }
516 }
517
518
519 static void printExports(const dyld3::MachOAnalyzer* ma)
520 {
521 printf(" -exports:\n");
522 printf(" offset symbol\n");
523 Diagnostics diag;
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);
531 if ( reExport )
532 printf(" [re-export] ");
533 else
534 printf(" 0x%08llX ", imageOffset);
535 printf("%s", symbolName);
536 if ( weakDef || threadLocal || resolver || abs ) {
537 bool needComma = false;
538 printf(" [");
539 if ( weakDef ) {
540 printf("weak_def");
541 needComma = true;
542 }
543 if ( threadLocal ) {
544 if ( needComma )
545 printf(", ");
546 printf("per-thread");
547 needComma = true;
548 }
549 if ( abs ) {
550 if ( needComma )
551 printf(", ");
552 printf("absolute");
553 needComma = true;
554 }
555 if ( resolver ) {
556 if ( needComma )
557 printf(", ");
558 printf("resolver=0x%08llX", other);
559 needComma = true;
560 }
561 printf("]");
562 }
563 if ( reExport ) {
564 if ( importName[0] == '\0' )
565 printf(" (from %s)", ordinalName(ma, (int)other));
566 else
567 printf(" (%s from %s)", importName, ordinalName(ma, (int)other));
568 }
569 printf("\n");
570 });
571
572 }
573
574 static void printObjC(const dyld3::MachOAnalyzer* ma)
575 {
576 Diagnostics diag;
577 const bool contentRebased = false;
578 const uint32_t pointerSize = ma->pointerSize();
579
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
587 break;
588 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
589 methodName = "### fairplay encrypted";
590 break;
591 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
592 methodName = "### protected section";
593 break;
594 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
595 methodName = "### unknown section";
596 break;
597 }
598 printf(" %10s 0x%08llX %s\n",
599 type, methodVMAddr, methodName);
600 };
601
602 printf(" -objc:\n");
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";
608 if (isMetaClass)
609 type = "meta-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
615 break;
616 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
617 className = "### fairplay encrypted";
618 break;
619 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
620 className = "### protected section";
621 break;
622 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
623 className = "### unknown section";
624 break;
625 }
626 printf(" %10s 0x%08llX 0x%08llX %s\n",
627 type, classVMAddr, objcClass.dataVMAddr, className);
628
629 // Now print the methods on this class
630 ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), contentRebased,
631 printMethod);
632 };
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
641 break;
642 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
643 categoryName = "### fairplay encrypted";
644 break;
645 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
646 categoryName = "### protected section";
647 break;
648 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
649 categoryName = "### unknown section";
650 break;
651 }
652 printf(" %10s 0x%08llX %s\n",
653 type, categoryVMAddr, categoryName);
654
655 // Now print the methods on this category
656 ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, contentRebased,
657 printMethod);
658 ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, contentRebased,
659 printMethod);
660 };
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
669 break;
670 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
671 protocolName = "### fairplay encrypted";
672 break;
673 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
674 protocolName = "### protected section";
675 break;
676 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
677 protocolName = "### unknown section";
678 break;
679 }
680 printf(" %10s 0x%08llX %s\n",
681 type, protocolVMAddr, protocolName);
682
683 // Now print the methods on this protocol
684 ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, contentRebased,
685 printMethod);
686 ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, contentRebased,
687 printMethod);
688 ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, contentRebased,
689 printMethod);
690 ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, contentRebased,
691 printMethod);
692 };
693 ma->forEachObjCClass(diag, contentRebased, printClass);
694 ma->forEachObjCCategory(diag, contentRebased, printCategory);
695 ma->forEachObjCProtocol(diag, contentRebased, printProtocol);
696 }
697
698 static void usage()
699 {
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"
706 );
707 }
708
709 static bool inStringVector(const std::vector<const char*>& vect, const char* target)
710 {
711 for (const char* str : vect) {
712 if ( strcmp(str, target) == 0 )
713 return true;
714 }
715 return false;
716 }
717
718
719 int main(int argc, const char* argv[])
720 {
721 if ( argc == 1 ) {
722 usage();
723 return 0;
724 }
725
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 ) {
732 if ( ++i < argc ) {
733 cmdLineArchs.push_back(argv[i]);
734 }
735 else {
736 fprintf(stderr, "-arch missing architecture name");
737 return 1;
738 }
739 }
740 }
741 else {
742 files.push_back(arg);
743 }
744 }
745 if ( files.size() == 0 ) {
746 usage();
747 return 0;
748 }
749
750 for (const char* path : files) {
751 Diagnostics diag;
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: ");
758 va_list list;
759 va_start(list, format);
760 vfprintf(stderr, format, list);
761 va_end(list);
762 printedError = true;
763 })) {
764 if (!printedError )
765 fprintf(stderr, "dyldinfo: %s: file not found\n", path);
766 return 1;
767 }
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)
779 platform = plat;
780 });
781 }
782 });
783 }
784 else {
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)
790 platform = plat;
791 });
792 }
793 else {
794 fprintf(stderr, "dyldinfo: %s: %s\n", path, diag.errorMessage());
795 return 1;
796 }
797 }
798 if ( archesForFile.empty() ) {
799 fprintf(stderr, "dyldinfo: %s: does not contain specified arch(s)\n", path);
800 return 1;
801 }
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());
807 return 1;
808 }
809 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)info.fileContent;
810 printf("%s [%s]:\n", path, sliceArch);
811
812 bool somethingPrinted = false;
813 for (int i=1; i < argc; ++i) {
814 const char* arg = argv[i];
815 if ( arg[0] != '-' )
816 continue;
817 if ( strcmp(arg, "-arch") == 0 ) {
818 // handled previously
819 ++i;
820 }
821 else if ( strcmp(arg, "-platform") == 0 ) {
822 printPlatforms(ma);
823 somethingPrinted = true;
824 }
825 else if ( strcmp(arg, "-segments") == 0 ) {
826 printSegments(ma);
827 somethingPrinted = true;
828 }
829 else if ( strcmp(arg, "-dependents") == 0 ) {
830 printDependents(ma);
831 somethingPrinted = true;
832 }
833 else if ( strcmp(arg, "-fixups") == 0 ) {
834 printFixups(ma);
835 somethingPrinted = true;
836 }
837 else if ( strcmp(arg, "-exports") == 0 ) {
838 printExports(ma);
839 somethingPrinted = true;
840 }
841 else if ( strcmp(arg, "-fixup_chains") == 0 ) {
842 printChains(ma);
843 somethingPrinted = true;
844 }
845 else if ( strcmp(arg, "-opcodes") == 0 ) {
846 }
847 else if ( strcmp(arg, "-shared_region") == 0 ) {
848 }
849 else if ( strcmp(arg, "-function_starts") == 0 ) {
850 }
851 else if ( strcmp(arg, "-data_in_code") == 0 ) {
852 }
853 else if ( strcmp(arg, "-objc") == 0 ) {
854 printObjC(ma);
855 somethingPrinted = true;
856 }
857 else {
858 fprintf(stderr, "unknown option: %s\n", arg);
859 return 1;
860 }
861 }
862 if ( !somethingPrinted ) {
863 printPlatforms(ma);
864 printSegments(ma);
865 printDependents(ma);
866 printFixups(ma);
867 printExports(ma);
868 printObjC(ma);
869 }
870
871 }
872 }
873
874 return 0;
875 }
876
877
878