dyld-750.5.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
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
47 static void versionToString(uint32_t value, char buffer[32])
48 {
49 if ( value == 0 )
50 strcpy(buffer, "n/a");
51 else if ( value & 0xFF )
52 sprintf(buffer, "%d.%d.%d", value >> 16, (value >> 8) & 0xFF, value & 0xFF);
53 else
54 sprintf(buffer, "%d.%d", value >> 16, (value >> 8) & 0xFF);
55 }
56
57 static void printPlatforms(const dyld3::MachOAnalyzer* ma)
58 {
59 printf(" -platform:\n");
60 printf(" platform minOS sdk\n");
61 ma->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
62 char osVers[32];
63 char sdkVers[32];
64 versionToString(minOS, osVers);
65 versionToString(sdk, sdkVers);
66 printf(" %15s %-7s %-7s\n", dyld3::MachOFile::platformName(platform), osVers, sdkVers);
67 });
68 }
69
70 static void printSegments(const dyld3::MachOAnalyzer* ma)
71 {
72 printf(" -segments:\n");
73 printf(" load-offset segment section sect-size seg-size perm\n");
74 __block const char* lastSegName = "";
75 __block uint64_t firstSegVmAddr = 0;
76 ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
77 if ( lastSegName[0] == '\0' )
78 firstSegVmAddr = sectInfo.segInfo.vmAddr;
79 if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) {
80 char r = (sectInfo.segInfo.protections & VM_PROT_READ) ? 'r' : '.';
81 char w = (sectInfo.segInfo.protections & VM_PROT_WRITE) ? 'w' : '.';
82 char x = (sectInfo.segInfo.protections & VM_PROT_EXECUTE) ? 'x' : '.';
83 printf(" 0x%08llX %-12s %6lluKB %c%c%c\n", sectInfo.segInfo.vmAddr - firstSegVmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, r, w, x);
84 lastSegName = sectInfo.segInfo.segName;
85 }
86 printf(" 0x%08llX %-16s %6llu\n", sectInfo.sectAddr-firstSegVmAddr, sectInfo.sectName, sectInfo.sectSize);
87
88 });
89
90 }
91
92
93 static void printDependents(const dyld3::MachOAnalyzer* ma)
94 {
95 printf(" -dependents:\n");
96 printf(" attributes load path\n");
97 ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
98 const char* attribute = "";
99 if ( isWeak )
100 attribute = "weak_import";
101 else if ( isReExport )
102 attribute = "re-export";
103 else if ( isUpward )
104 attribute = "upward";
105 printf(" %-12s %s\n", attribute, loadPath);
106 });
107 }
108
109 static const char* rebaseTypeName(uint8_t type)
110 {
111 switch (type ){
112 case REBASE_TYPE_POINTER:
113 return "rebase pointer";
114 case REBASE_TYPE_TEXT_ABSOLUTE32:
115 return "rebase text abs32";
116 case REBASE_TYPE_TEXT_PCREL32:
117 return "rebase text rel32";
118 }
119 return "!!unknown!!";
120 }
121
122 static const char* bindTypeName(uint8_t type)
123 {
124 switch (type ){
125 case BIND_TYPE_POINTER:
126 return "bind pointer";
127 case BIND_TYPE_TEXT_ABSOLUTE32:
128 return "bind text abs32";
129 case BIND_TYPE_TEXT_PCREL32:
130 return "bind text rel32";
131 }
132 return "!!unknown!!";
133 }
134
135 static const char* pointerFormat(uint16_t format)
136 {
137 switch (format) {
138 case DYLD_CHAINED_PTR_ARM64E:
139 return "authenticated arm64e";
140 case DYLD_CHAINED_PTR_ARM64E_OFFSET:
141 return "authenticated arm64e offset";
142 case DYLD_CHAINED_PTR_64:
143 return "generic 64-bit";
144 case DYLD_CHAINED_PTR_64_OFFSET:
145 return "generic 64-bit offset";
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 pageIndex=0; pageIndex < seg->page_count; ++pageIndex) {
171 uint16_t offsetInPage = seg->page_start[pageIndex];
172 if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE )
173 continue;
174 if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) {
175 // 32-bit chains which may need multiple starts per page
176 uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI;
177 bool chainEnd = false;
178 while (!chainEnd) {
179 chainEnd = (seg->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST);
180 offsetInPage = (seg->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
181 printf(" start[% 2d]: 0x%04X\n", pageIndex, offsetInPage);
182 ++overflowIndex;
183 }
184 }
185 else {
186 // one chain per page
187 printf(" start[% 2d]: 0x%04X\n", pageIndex, offsetInPage);
188 }
189 }
190 }
191 });
192 }
193
194
195 static void printChainDetails(const dyld3::MachOAnalyzer* ma)
196 {
197 __block Diagnostics diag;
198 ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
199 ma->forEachFixupInAllChains(diag, starts, true, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
200 uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
201 switch (segInfo->pointer_format) {
202 case DYLD_CHAINED_PTR_ARM64E:
203 case DYLD_CHAINED_PTR_ARM64E_OFFSET:
204 if ( fixupLoc->arm64e.authRebase.auth ) {
205 if ( fixupLoc->arm64e.authBind.bind ) {
206 printf(" 0x%08llX: raw: 0x%016llX auth-bind: (next: %03d, key: %s, addrDiv: %d, diversity: 0x%04X, ordinal: %04X)\n", vmOffset, fixupLoc->raw64,
207 fixupLoc->arm64e.authBind.next, fixupLoc->arm64e.keyName(),
208 fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.ordinal);
209 }
210 else {
211 printf(" 0x%08llX: raw: 0x%016llX auth-rebase: (next: %03d, key: %s, addrDiv: %d, diversity: 0x%04X, target: 0x%08X)\n", vmOffset, fixupLoc->raw64,
212 fixupLoc->arm64e.authRebase.next, fixupLoc->arm64e.keyName(),
213 fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authRebase.target);
214 }
215 }
216 else {
217 if ( fixupLoc->arm64e.rebase.bind ) {
218 printf(" 0x%08llX: raw: 0x%016llX bind: (next: %03d, ordinal: %04X, addend: %d)\n", vmOffset, fixupLoc->raw64,
219 fixupLoc->arm64e.bind.next, fixupLoc->arm64e.bind.ordinal, fixupLoc->arm64e.bind.addend);
220 }
221 else {
222 printf(" 0x%08llX: raw: 0x%016llX rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64,
223 fixupLoc->arm64e.rebase.next, fixupLoc->arm64e.rebase.target, fixupLoc->arm64e.rebase.high8);
224 }
225 }
226 break;
227 case DYLD_CHAINED_PTR_64:
228 case DYLD_CHAINED_PTR_64_OFFSET:
229 if ( fixupLoc->generic64.rebase.bind ) {
230 printf(" 0x%08llX: raw: 0x%016llX bind: (next: %03d, ordinal: %06X, addend: %d)\n", vmOffset, fixupLoc->raw64,
231 fixupLoc->generic64.bind.next, fixupLoc->generic64.bind.ordinal, fixupLoc->generic64.bind.addend);
232 }
233 else {
234 printf(" 0x%08llX: raw: 0x%016llX rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64,
235 fixupLoc->generic64.rebase.next, fixupLoc->generic64.rebase.target, fixupLoc->generic64.rebase.high8);
236 }
237 break;
238 case DYLD_CHAINED_PTR_32:
239 if ( fixupLoc->generic32.bind.bind ) {
240 printf(" 0x%08llX: raw: 0x%08X bind: (next:%02d ordinal:%05X addend:%d)\n", vmOffset, fixupLoc->raw32,
241 fixupLoc->generic32.bind.next, fixupLoc->generic32.bind.ordinal, fixupLoc->generic32.bind.addend);
242 }
243 else if ( fixupLoc->generic32.rebase.target > segInfo->max_valid_pointer ) {
244 uint32_t bias = (0x04000000 + segInfo->max_valid_pointer)/2;
245 uint32_t value = fixupLoc->generic32.rebase.target - bias;
246 printf(" 0x%08llX: raw: 0x%08X nonptr: (next:%02d value: 0x%08X)\n", vmOffset, fixupLoc->raw32,
247 fixupLoc->generic32.rebase.next, value);
248 }
249 else {
250 printf(" 0x%08llX: raw: 0x%08X rebase: (next:%02d target: 0x%07X)\n", vmOffset, fixupLoc->raw32,
251 fixupLoc->generic32.rebase.next, fixupLoc->generic32.rebase.target);
252 }
253 break;
254 default:
255 fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
256 break;
257 }
258 });
259 });
260 if ( diag.hasError() )
261 fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
262 }
263
264
265 struct FixupInfo
266 {
267 const char* segName;
268 std::string sectName;
269 uint64_t address;
270 const char* type;
271 uint64_t targetValue;
272 const char* targetDylib;
273 const char* targetSymbolName;
274 uint64_t targetAddend;
275 bool targetWeakImport;
276 };
277
278
279
280 static const char* ordinalName(const dyld3::MachOAnalyzer* ma, int libraryOrdinal)
281 {
282 static int sLastOrdinal = -100;
283 static const char* sLastString = nullptr;
284 if ( libraryOrdinal > 0 ) {
285 if ( libraryOrdinal != sLastOrdinal ) {
286 const char* path = ma->dependentDylibLoadPath(libraryOrdinal-1);
287 if ( path == nullptr )
288 return "ordinal-too-large";
289 const char* leafName = path;
290 if ( const char* lastSlash = strrchr(path, '/') )
291 leafName = lastSlash+1;
292 sLastOrdinal = libraryOrdinal;
293 sLastString = leafName;
294 }
295 return sLastString;
296 }
297 else {
298 switch ( libraryOrdinal) {
299 case BIND_SPECIAL_DYLIB_SELF:
300 return "this-image";
301 case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
302 return "main-executable";
303 case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
304 return "flat-namespace";
305 case BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
306 return "weak-coalesce";
307 }
308 }
309 return "unknown-ordinal";
310 }
311
312
313 class SectionFinder
314 {
315 public:
316 SectionFinder(const dyld3::MachOAnalyzer* ma);
317 const char* segmentName(uint64_t vmOffset) const;
318 const char* sectionName(uint64_t vmOffset) const;
319 uint64_t baseAddress() const { return _baseAddress; }
320 uint64_t currentSectionAddress() const { return _lastSection.sectAddr; }
321 bool isNewSection(uint64_t vmOffset) const;
322
323 private:
324 void updateLastSection(uint64_t vmOffset) const;
325
326 const dyld3::MachOAnalyzer* _ma;
327 uint64_t _baseAddress;
328 mutable dyld3::MachOFile::SectionInfo _lastSection;
329 mutable char _lastSectName[20];
330 };
331
332 SectionFinder::SectionFinder(const dyld3::MachOAnalyzer* ma)
333 : _ma(ma)
334 {
335 _baseAddress = ma->preferredLoadAddress();
336 _lastSection.sectAddr = 0;
337 _lastSection.sectSize = 0;
338 }
339
340 bool SectionFinder::isNewSection(uint64_t vmOffset) const
341 {
342 uint64_t vmAddr = _baseAddress + vmOffset;
343 return ( (vmAddr < _lastSection.sectAddr) || (vmAddr >= _lastSection.sectAddr+_lastSection.sectSize) );
344 }
345
346 void SectionFinder::updateLastSection(uint64_t vmOffset) const
347 {
348 if ( isNewSection(vmOffset) ) {
349 uint64_t vmAddr = _baseAddress + vmOffset;
350 _ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& sectStop) {
351 if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) {
352 _lastSection = sectInfo;
353 strcpy(_lastSectName, _lastSection.sectName);
354 sectStop = true;
355 }
356 });
357 }
358 }
359
360 const char* SectionFinder::segmentName(uint64_t vmOffset) const
361 {
362 updateLastSection(vmOffset);
363 return _lastSection.segInfo.segName;
364 }
365
366 const char* SectionFinder::sectionName(uint64_t vmOffset) const
367 {
368 updateLastSection(vmOffset);
369 return _lastSectName;
370 }
371
372
373
374 static void printPreloadChainedFixups(const dyld3::MachOAnalyzer* ma)
375 {
376 printf(" segment section address type (dvrsty addr key) target\n");
377 SectionFinder namer(ma);
378 uint64_t sectionSize;
379 const dyld_chained_starts_offsets* startsSection = (dyld_chained_starts_offsets*)ma->findSectionContent("__TEXT", "__chain_starts", sectionSize);
380 if ( startsSection != nullptr ) {
381 switch (startsSection->pointer_format) {
382 case DYLD_CHAINED_PTR_32_FIRMWARE:
383 for (uint32_t startIndex=0; startIndex < startsSection->starts_count; ++startIndex) {
384 const dyld_chained_ptr_32_firmware_rebase* p = (dyld_chained_ptr_32_firmware_rebase*)(((uint8_t*)ma)+ startsSection->chain_starts[startIndex]);
385 bool done = false;
386 while (!done) {
387 uint64_t vmOffset = ((uint8_t*)p - (uint8_t*)ma);
388 printf(" %-12s %-16s 0x%08llX %16s 0x%08X\n",
389 namer.segmentName(vmOffset), namer.sectionName(vmOffset), namer.baseAddress()+vmOffset,
390 "rebase pointer", p->target);
391 done = (p->next == 0);
392 p += p->next;
393 }
394 }
395 }
396
397 }
398 }
399
400
401
402 struct FixupTarget
403 {
404 uint64_t value;
405 const char* dylib;
406 const char* symbolName;
407 uint64_t addend;
408 bool weakImport;
409 };
410
411
412 static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
413 {
414 // build array of targets
415 __block Diagnostics diag;
416 __block std::vector<FixupTarget> targets;
417 ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
418 FixupTarget target;
419 target.value = 0;
420 target.dylib = ordinalName(ma, libOrdinal);
421 target.symbolName = symbolName;
422 target.addend = addend;
423 target.weakImport = weakImport;
424 targets.push_back(target);
425 });
426 if ( diag.hasError() )
427 return;
428
429 uint64_t baseAddress = ma->preferredLoadAddress();
430
431 printf(" segment section address type (dvrsty addr key) target\n");
432 SectionFinder namer(ma);
433 ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
434 ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
435 uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
436 switch (segInfo->pointer_format) {
437 case DYLD_CHAINED_PTR_ARM64E:
438 if ( fixupLoc->arm64e.authRebase.auth ) {
439 if ( fixupLoc->arm64e.authBind.bind ) {
440 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
441 const char* weakImportString = bindTarget.weakImport ? " [weak-import]" : "";
442 if ( bindTarget.addend )
443 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) %s/%s + 0x%llX%s\n",
444 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr",
445 fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
446 fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, bindTarget.addend, weakImportString);
447 else
448 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) %s/%s%s\n",
449 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr",
450 fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
451 fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, weakImportString);
452 }
453 else {
454 uint64_t targetAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
455 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) 0x%08llX\n",
456 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "rebase authptr",
457 fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
458 fixupLoc->arm64e.keyName(), targetAddr);
459 }
460 }
461 else {
462 if ( fixupLoc->arm64e.rebase.bind ) {
463 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
464 uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
465 const char* weakImportString = bindTarget.weakImport ? " [weak-import]" : "";
466 if ( fullAddend )
467 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX%s\n",
468 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
469 "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
470 else
471 printf(" %-12s %-16s 0x%08llX %16s %s/%s%s\n",
472 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
473 "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
474 }
475 else {
476 uint64_t targetAddr = fixupLoc->arm64e.unpackTarget();
477 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n",
478 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
479 "rebase pointer", targetAddr);
480 }
481 }
482 break;
483 case DYLD_CHAINED_PTR_ARM64E_OFFSET:
484 if ( fixupLoc->arm64e.authRebase.auth ) {
485 if ( fixupLoc->arm64e.authBind.bind ) {
486 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
487 const char* weakImportString = bindTarget.weakImport ? " [weak-import]" : "";
488 if ( bindTarget.addend )
489 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) %s/%s + 0x%llX%s\n",
490 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr",
491 fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
492 fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, bindTarget.addend, weakImportString);
493 else
494 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) %s/%s%s\n",
495 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr",
496 fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
497 fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, weakImportString);
498 }
499 else {
500 uint64_t targetAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
501 printf(" %-12s %-16s 0x%08llX %16s (0x%04X %d %s) 0x%08llX\n",
502 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "rebase authptr",
503 fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
504 fixupLoc->arm64e.keyName(), targetAddr);
505 }
506 }
507 else {
508 if ( fixupLoc->arm64e.rebase.bind ) {
509 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
510 uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
511 const char* weakImportString = bindTarget.weakImport ? " [weak-import]" : "";
512 if ( fullAddend )
513 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX%s\n",
514 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
515 "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
516 else
517 printf(" %-12s %-16s 0x%08llX %16s %s/%s%s\n",
518 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
519 "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
520 }
521 else {
522 uint64_t targetAddr = fixupLoc->arm64e.unpackTarget() + baseAddress;
523 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n",
524 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
525 "rebase pointer", targetAddr);
526 }
527 }
528 break;
529 case DYLD_CHAINED_PTR_64:
530 if ( fixupLoc->generic64.rebase.bind ) {
531 const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
532 uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
533 const char* weakImportString = bindTarget.weakImport ? " [weak-import]" : "";
534 if ( fullAddend )
535 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX%s\n",
536 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
537 "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
538 else
539 printf(" %-12s %-16s 0x%08llX %16s %s/%s%s\n",
540 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
541 "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
542 }
543 else {
544 uint64_t targetAddr = fixupLoc->generic64.unpackedTarget();
545 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n",
546 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
547 "rebase pointer", targetAddr);
548 }
549 break;
550 case DYLD_CHAINED_PTR_64_OFFSET:
551 if ( fixupLoc->generic64.rebase.bind ) {
552 const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
553 uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
554 const char* weakImportString = bindTarget.weakImport ? " [weak-import]" : "";
555 if ( fullAddend )
556 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%llX%s\n",
557 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
558 "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
559 else
560 printf(" %-12s %-16s 0x%08llX %16s %s/%s%s\n",
561 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
562 "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
563 }
564 else {
565 uint64_t targetAddr = fixupLoc->generic64.unpackedTarget() + baseAddress;
566 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n",
567 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
568 "rebase pointer", targetAddr);
569 }
570 break;
571 case DYLD_CHAINED_PTR_32:
572 if ( fixupLoc->generic32.rebase.bind ) {
573 const FixupTarget& bindTarget = targets[fixupLoc->generic32.bind.ordinal];
574 uint32_t fullAddend = (uint32_t)bindTarget.addend + fixupLoc->generic32.bind.addend;
575 const char* weakImportString = bindTarget.weakImport ? " [weak-import]" : "";
576 if ( fullAddend )
577 printf(" %-12s %-16s 0x%08llX %16s %s/%s + 0x%X%s\n",
578 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
579 "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
580 else
581 printf(" %-12s %-16s 0x%08llX %16s %s/%s%s\n",
582 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
583 "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
584 }
585 else {
586 uint32_t targetAddr = fixupLoc->generic32.rebase.target;
587 printf(" %-12s %-16s 0x%08llX %16s 0x%08X\n",
588 namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
589 "rebase pointer", targetAddr);
590 }
591 break;
592 default:
593 fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
594 break;
595 }
596 });
597 });
598 if ( diag.hasError() )
599 fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
600 }
601
602 static void printOpcodeFixups(const dyld3::MachOAnalyzer* ma)
603 {
604 Diagnostics diag;
605 __block std::vector<FixupInfo> fixups;
606 SectionFinder namer(ma);
607 ma->forEachRebase(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
608 bool segIndexSet, uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) {
609 const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
610 uint64_t locVmAddr = segment.vmAddr + segOffset;
611 uint64_t runtimeOffset = locVmAddr - namer.baseAddress();
612 const uint8_t* loc = ((uint8_t*)ma + runtimeOffset);
613 uint64_t value = (pointerSize == 8) ? *((uint64_t*)(loc)) : *((uint32_t*)(loc));
614 FixupInfo fixup;
615 fixup.segName = namer.segmentName(runtimeOffset);
616 fixup.sectName = namer.sectionName(runtimeOffset);
617 fixup.address = locVmAddr;
618 fixup.type = rebaseTypeName(type);
619 fixup.targetValue = value;
620 fixup.targetDylib = nullptr;
621 fixup.targetSymbolName = nullptr;
622 fixup.targetAddend = 0;
623 fixup.targetWeakImport = false;
624 fixups.push_back(fixup);
625 });
626
627 ma->forEachBind(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
628 bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
629 uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset,
630 uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
631 const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
632 uint64_t locVmAddr = segment.vmAddr + segOffset;
633 uint64_t runtimeOffset = locVmAddr - namer.baseAddress();
634 FixupInfo fixup;
635 fixup.segName = namer.segmentName(runtimeOffset);
636 fixup.sectName = namer.sectionName(runtimeOffset);
637 fixup.address = locVmAddr;
638 fixup.type = bindTypeName(type);
639 fixup.targetValue = 0;
640 fixup.targetDylib = ordinalName(ma, libOrdinal);
641 fixup.targetSymbolName = symbolName;
642 fixup.targetAddend = addend;
643 fixup.targetWeakImport = weakImport;
644 fixups.push_back(fixup);
645 },^(const char* symbolName) {
646 },^() { });
647
648
649 std::sort(fixups.begin(), fixups.end(), [](const FixupInfo& l, const FixupInfo& r) {
650 if ( &l == &r )
651 return false;
652 if ( l.address == r.address )
653 return (l.targetSymbolName == nullptr);
654 return ( l.address < r.address );
655 });
656
657 printf(" segment section address type target\n");
658 for (const FixupInfo& fixup : fixups) {
659 if ( fixup.targetSymbolName == nullptr )
660 printf(" %-12s %-16s 0x%08llX %16s 0x%08llX\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetValue);
661 else if ( fixup.targetAddend != 0 )
662 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);
663 else if ( fixup.targetWeakImport )
664 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);
665 else
666 printf(" %-12s %-16s 0x%08llX %16s %s/%s\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName);
667 }
668
669
670 }
671
672 static inline std::string decimal(int64_t value) {
673 char buff[64];
674 sprintf(buff, "%lld", value);
675 return buff;
676 }
677
678 static std::string rebaseTargetString(const dyld3::MachOAnalyzer* ma, uint64_t vmAddr)
679 {
680 uint64_t targetLoadAddr = (uint64_t)ma+vmAddr;
681 const char* targetSymbolName;
682 uint64_t targetSymbolLoadAddr;
683 if ( ma->findClosestSymbol(targetLoadAddr, &targetSymbolName, &targetSymbolLoadAddr) ) {
684 uint64_t delta = targetLoadAddr - targetSymbolLoadAddr;
685 if ( delta == 0 ) {
686 return targetSymbolName;
687 }
688 else {
689 return std::string(targetSymbolName) + std::string("+") + decimal(delta);
690 }
691 }
692 else {
693 __block std::string result;
694 ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
695 if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) {
696 if ( (sectInfo.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) {
697 const char* cstring = (char*)ma + (vmAddr-ma->preferredLoadAddress());
698 result = std::string("\"") + cstring + std::string("\"");
699 }
700 else {
701 result = std::string(sectInfo.segInfo.segName) + "/" + sectInfo.sectName + "+" + decimal(vmAddr - sectInfo.sectAddr);
702 }
703 }
704 });
705 return result;
706 }
707 }
708
709 static void printSymbolicChainedFixups(const dyld3::MachOAnalyzer* ma)
710 {
711 // build array of targets
712 __block Diagnostics diag;
713 __block std::vector<FixupTarget> targets;
714 ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
715 FixupTarget target;
716 target.value = 0;
717 target.dylib = ordinalName(ma, libOrdinal);
718 target.symbolName = symbolName;
719 target.addend = addend;
720 target.weakImport = weakImport;
721 targets.push_back(target);
722 });
723 if ( diag.hasError() )
724 return;
725
726 // walk all fixup chains
727 uint64_t baseAddress = ma->preferredLoadAddress();
728 SectionFinder sectionInfo(ma);
729 __block uint64_t lastSymbolVmOffset = 0;
730 __block bool lastSymbolIsSectionStart = false;
731 ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
732 ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
733 uint64_t fixupVmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
734 if ( sectionInfo.isNewSection(fixupVmOffset) ) {
735 printf(" 0x%08llX %-12s %-16s \n", sectionInfo.currentSectionAddress(), sectionInfo.segmentName(fixupVmOffset), sectionInfo.sectionName(fixupVmOffset));
736 lastSymbolVmOffset = sectionInfo.currentSectionAddress()-sectionInfo.baseAddress();
737 lastSymbolIsSectionStart = true;
738 }
739 const char* symbolName;
740 uint64_t symbolLoadAddr = 0;
741 if ( ma->findClosestSymbol((uint64_t)fixupLoc, &symbolName, &symbolLoadAddr) ) {
742 uint64_t symbolVmOffset = symbolLoadAddr - (uint64_t)ma;
743 if ( (symbolVmOffset != lastSymbolVmOffset) || lastSymbolIsSectionStart ) {
744 printf(" %s:\n", symbolName);
745 lastSymbolVmOffset = symbolVmOffset;
746 lastSymbolIsSectionStart = false;
747 }
748 }
749 const char* fixupKind = "";
750 std::string fixupTarget;
751 char authInfo[64];
752 switch (segInfo->pointer_format) {
753 case DYLD_CHAINED_PTR_ARM64E:
754 if ( fixupLoc->arm64e.authRebase.auth ) {
755 sprintf(authInfo, "(0x%04X %d %s)", fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.keyName());
756 if ( fixupLoc->arm64e.authBind.bind ) {
757 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
758 fixupKind = "bind authptr";
759 fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
760 if ( bindTarget.addend )
761 fixupTarget += std::string("+") + decimal(bindTarget.addend);
762 if ( bindTarget.weakImport )
763 fixupTarget += " [weak-import]";
764 }
765 else {
766 uint64_t targetVmAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
767 fixupKind = "rebase authptr";
768 fixupTarget = rebaseTargetString(ma, targetVmAddr);
769 }
770 }
771 else {
772 authInfo[0] = '\0';
773 if ( fixupLoc->arm64e.rebase.bind ) {
774 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
775 uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
776 fixupKind = "bind pointer";
777 fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
778 if ( fullAddend )
779 fixupTarget += std::string("+") + decimal(fullAddend);
780 if ( bindTarget.weakImport )
781 fixupTarget += " [weak-import]";
782 }
783 else {
784 uint64_t targetVmAddr = fixupLoc->arm64e.unpackTarget();
785 fixupKind = "rebase pointer";
786 fixupTarget = rebaseTargetString(ma, targetVmAddr);
787 }
788 }
789 printf(" +0x%04llX %16s %30s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, authInfo, fixupTarget.c_str());
790 break;
791 case DYLD_CHAINED_PTR_ARM64E_OFFSET:
792 if ( fixupLoc->arm64e.authRebase.auth ) {
793 sprintf(authInfo, "(0x%04X %d %s)", fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.keyName());
794 if ( fixupLoc->arm64e.authBind.bind ) {
795 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
796 fixupKind = "bind authptr";
797 fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
798 if ( bindTarget.addend )
799 fixupTarget += std::string("+") + decimal(bindTarget.addend);
800 if ( bindTarget.weakImport )
801 fixupTarget += " [weak-import]";
802 }
803 else {
804 uint64_t targetVmAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
805 fixupKind = "rebase authptr";
806 fixupTarget = rebaseTargetString(ma, targetVmAddr);
807 }
808 }
809 else {
810 authInfo[0] = '\0';
811 if ( fixupLoc->arm64e.rebase.bind ) {
812 const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
813 uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
814 fixupKind = "bind pointer";
815 fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
816 if ( fullAddend )
817 fixupTarget += std::string("+") + decimal(fullAddend);
818 if ( bindTarget.weakImport )
819 fixupTarget += " [weak-import]";
820 }
821 else {
822 uint64_t targetVmAddr = fixupLoc->arm64e.unpackTarget() + baseAddress;
823 fixupKind = "rebase pointer";
824 fixupTarget = rebaseTargetString(ma, targetVmAddr);
825 }
826 }
827 printf(" +0x%04llX %16s %30s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, authInfo, fixupTarget.c_str());
828 break;
829 case DYLD_CHAINED_PTR_64:
830 if ( fixupLoc->generic64.rebase.bind ) {
831 const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
832 fixupKind = "bind pointer";
833 uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
834 fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
835 if ( fullAddend )
836 fixupTarget += std::string("+") + decimal(fullAddend);
837 if ( bindTarget.weakImport )
838 fixupTarget += " [weak-import]";
839 }
840 else {
841 uint64_t targetVmAddr = fixupLoc->generic64.unpackedTarget();
842 fixupKind = "rebase pointer";
843 fixupTarget = rebaseTargetString(ma, targetVmAddr);
844 }
845 printf(" +0x%04llX %16s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
846 break;
847 case DYLD_CHAINED_PTR_64_OFFSET:
848 if ( fixupLoc->generic64.rebase.bind ) {
849 const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
850 fixupKind = "bind pointer";
851 uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
852 fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
853 if ( fullAddend )
854 fixupTarget += std::string("+") + decimal(fullAddend);
855 if ( bindTarget.weakImport )
856 fixupTarget += " [weak-import]";
857 }
858 else {
859 uint64_t targetVmAddr = fixupLoc->generic64.unpackedTarget() + baseAddress;
860 fixupKind = "rebase pointer";
861 fixupTarget = rebaseTargetString(ma, targetVmAddr);
862 }
863 printf(" +0x%04llX %16s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
864 break;
865 case DYLD_CHAINED_PTR_32:
866 if ( fixupLoc->generic32.rebase.bind ) {
867 const FixupTarget& bindTarget = targets[fixupLoc->generic32.bind.ordinal];
868 uint32_t fullAddend = (uint32_t)bindTarget.addend + fixupLoc->generic32.bind.addend;
869 fixupKind = "bind pointer";
870 fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
871 if ( fullAddend )
872 fixupTarget += std::string("+") + decimal(fullAddend);
873 if ( bindTarget.weakImport )
874 fixupTarget += " [weak-import]";
875 }
876 else {
877 uint32_t targetAddr = fixupLoc->generic32.rebase.target;
878 fixupKind = "rebase pointer";
879 fixupTarget = rebaseTargetString(ma, targetAddr);
880 }
881 printf(" +0x%04llX %16s %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
882 break;
883 default:
884 fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
885 break;
886 }
887 });
888 });
889 if ( diag.hasError() )
890 fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
891 }
892
893 struct SymbolicFixupInfo
894 {
895 uint64_t address;
896 const char* kind;
897 std::string target;
898 };
899
900 static void printSymbolicOpcodeFixups(const dyld3::MachOAnalyzer* ma)
901 {
902 Diagnostics diag;
903 __block std::vector<SymbolicFixupInfo> fixups;
904 SectionFinder namer(ma);
905 ma->forEachRebase(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
906 bool segIndexSet, uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) {
907 const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
908 uint64_t locVmAddr = segment.vmAddr + segOffset;
909 uint64_t runtimeOffset = locVmAddr - namer.baseAddress();
910 const uint8_t* loc = ((uint8_t*)ma + runtimeOffset);
911 uint64_t value = (pointerSize == 8) ? *((uint64_t*)(loc)) : *((uint32_t*)(loc));
912 SymbolicFixupInfo fixup;
913 fixup.address = locVmAddr;
914 fixup.kind = rebaseTypeName(type);
915 fixup.target = rebaseTargetString(ma, value);
916 fixups.push_back(fixup);
917 });
918
919 ma->forEachBind(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
920 bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
921 uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset,
922 uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
923 const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
924 uint64_t locVmAddr = segment.vmAddr + segOffset;
925 SymbolicFixupInfo fixup;
926 fixup.address = locVmAddr;
927 fixup.kind = bindTypeName(type);
928 fixup.target = std::string(ordinalName(ma, libOrdinal)) + "/" + symbolName;
929 if ( addend != 0 )
930 fixup.target += std::string("+") + decimal(addend);
931 if ( weakImport )
932 fixup.target += " [weak-import]";
933 fixups.push_back(fixup);
934 },^(const char* symbolName) {
935 },^() { });
936
937
938 std::sort(fixups.begin(), fixups.end(), [](const SymbolicFixupInfo& l, const SymbolicFixupInfo& r) {
939 if ( &l == &r )
940 return false;
941 return ( l.address < r.address );
942 });
943
944 SectionFinder sectionTracker(ma);
945 uint64_t lastSymbolVmOffset = 0;
946 for (const SymbolicFixupInfo& fixup : fixups) {
947 uint64_t vmOffset = fixup.address;
948 uint64_t vmAddr = sectionTracker.baseAddress() + vmOffset;
949 if ( sectionTracker.isNewSection(vmOffset) ) {
950 printf(" 0x%08llX %-12s %-16s \n", vmAddr, sectionTracker.segmentName(vmOffset), sectionTracker.sectionName(vmOffset));
951 lastSymbolVmOffset = vmOffset;
952 }
953 const char* symbolName;
954 uint64_t symbolLoadAddr = 0;
955 if ( ma->findClosestSymbol((uint64_t)ma+vmOffset, &symbolName, &symbolLoadAddr) ) {
956 uint64_t symbolVmOffset = symbolLoadAddr - (uint64_t)ma;
957 if ( symbolVmOffset != lastSymbolVmOffset ) {
958 printf(" %s:\n", symbolName);
959 lastSymbolVmOffset = symbolVmOffset;
960 }
961 }
962 printf(" +0x%04llX %16s %s\n", vmOffset - lastSymbolVmOffset, fixup.kind, fixup.target.c_str());
963 }
964 }
965
966 static void printFixups(const dyld3::MachOAnalyzer* ma)
967 {
968 printf(" -fixups:\n");
969 if ( ma->isPreload() || (ma->isStaticExecutable() && !ma->hasChainedFixups()) ) {
970 printPreloadChainedFixups(ma);
971 }
972 else if ( ma->hasChainedFixups() ) {
973 printChainedFixups(ma);
974 }
975 else {
976 printOpcodeFixups(ma);
977 }
978 }
979
980
981 static void printSymbolicFixups(const dyld3::MachOAnalyzer* ma)
982 {
983 printf(" -symbolic_fixups:\n");
984 if ( ma->isPreload() || ma->isStaticExecutable() ) {
985 printPreloadChainedFixups(ma);
986 }
987 else if ( ma->hasChainedFixups() ) {
988 printSymbolicChainedFixups(ma);
989 }
990 else {
991 printSymbolicOpcodeFixups(ma);
992 }
993 }
994
995
996
997 static void printExports(const dyld3::MachOAnalyzer* ma)
998 {
999 printf(" -exports:\n");
1000 printf(" offset symbol\n");
1001 Diagnostics diag;
1002 ma->forEachExportedSymbol(diag, ^(const char* symbolName, uint64_t imageOffset, uint64_t flags, uint64_t other, const char* importName, bool& stop) {
1003 //printf("0x%08llX %s\n", imageOffset, symbolName);
1004 const bool reExport = (flags & EXPORT_SYMBOL_FLAGS_REEXPORT);
1005 const bool weakDef = (flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION);
1006 const bool resolver = (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER);
1007 const bool threadLocal = ((flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL);
1008 const bool abs = ((flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE);
1009 if ( reExport )
1010 printf(" [re-export] ");
1011 else
1012 printf(" 0x%08llX ", imageOffset);
1013 printf("%s", symbolName);
1014 if ( weakDef || threadLocal || resolver || abs ) {
1015 bool needComma = false;
1016 printf(" [");
1017 if ( weakDef ) {
1018 printf("weak_def");
1019 needComma = true;
1020 }
1021 if ( threadLocal ) {
1022 if ( needComma )
1023 printf(", ");
1024 printf("per-thread");
1025 needComma = true;
1026 }
1027 if ( abs ) {
1028 if ( needComma )
1029 printf(", ");
1030 printf("absolute");
1031 needComma = true;
1032 }
1033 if ( resolver ) {
1034 if ( needComma )
1035 printf(", ");
1036 printf("resolver=0x%08llX", other);
1037 needComma = true;
1038 }
1039 printf("]");
1040 }
1041 if ( reExport ) {
1042 if ( importName[0] == '\0' )
1043 printf(" (from %s)", ordinalName(ma, (int)other));
1044 else
1045 printf(" (%s from %s)", importName, ordinalName(ma, (int)other));
1046 }
1047 printf("\n");
1048 });
1049
1050 }
1051
1052 static void printObjC(const dyld3::MachOAnalyzer* ma)
1053 {
1054 Diagnostics diag;
1055 const bool contentRebased = false;
1056 const uint32_t pointerSize = ma->pointerSize();
1057
1058 auto printMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
1059 const char* type = "method";
1060 dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
1061 const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
1062 switch (methodNameResult) {
1063 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint:
1064 // The string is already valid
1065 break;
1066 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
1067 methodName = "### fairplay encrypted";
1068 break;
1069 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
1070 methodName = "### protected section";
1071 break;
1072 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
1073 methodName = "### unknown section";
1074 break;
1075 }
1076 printf(" %10s 0x%08llX %s\n",
1077 type, methodVMAddr, methodName);
1078 };
1079
1080 printf(" -objc:\n");
1081 printf(" type vmaddr data-vmaddr name\n");
1082 auto printClass = ^(Diagnostics& diag, uint64_t classVMAddr,
1083 uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
1084 const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
1085 const char* type = "class";
1086 if (isMetaClass)
1087 type = "meta-class";
1088 dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
1089 const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
1090 switch (classNameResult) {
1091 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint:
1092 // The string is already valid
1093 break;
1094 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
1095 className = "### fairplay encrypted";
1096 break;
1097 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
1098 className = "### protected section";
1099 break;
1100 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
1101 className = "### unknown section";
1102 break;
1103 }
1104 printf(" %10s 0x%08llX 0x%08llX %s\n",
1105 type, classVMAddr, objcClass.dataVMAddr, className);
1106
1107 // Now print the methods on this class
1108 ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), contentRebased,
1109 printMethod);
1110 };
1111 auto printCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
1112 const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
1113 const char* type = "category";
1114 dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
1115 const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
1116 switch (categoryNameResult) {
1117 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint:
1118 // The string is already valid
1119 break;
1120 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
1121 categoryName = "### fairplay encrypted";
1122 break;
1123 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
1124 categoryName = "### protected section";
1125 break;
1126 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
1127 categoryName = "### unknown section";
1128 break;
1129 }
1130 printf(" %10s 0x%08llX %s\n",
1131 type, categoryVMAddr, categoryName);
1132
1133 // Now print the methods on this category
1134 ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, contentRebased,
1135 printMethod);
1136 ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, contentRebased,
1137 printMethod);
1138 };
1139 auto printProtocol = ^(Diagnostics& diag, uint64_t protocolVMAddr,
1140 const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol) {
1141 const char* type = "protocol";
1142 dyld3::MachOAnalyzer::PrintableStringResult protocolNameResult;
1143 const char* protocolName = ma->getPrintableString(objCProtocol.nameVMAddr, protocolNameResult);
1144 switch (protocolNameResult) {
1145 case dyld3::MachOAnalyzer::PrintableStringResult::CanPrint:
1146 // The string is already valid
1147 break;
1148 case dyld3::MachOAnalyzer::PrintableStringResult::FairPlayEncrypted:
1149 protocolName = "### fairplay encrypted";
1150 break;
1151 case dyld3::MachOAnalyzer::PrintableStringResult::ProtectedSection:
1152 protocolName = "### protected section";
1153 break;
1154 case dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection:
1155 protocolName = "### unknown section";
1156 break;
1157 }
1158 printf(" %10s 0x%08llX %s\n",
1159 type, protocolVMAddr, protocolName);
1160
1161 // Now print the methods on this protocol
1162 ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, contentRebased,
1163 printMethod);
1164 ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, contentRebased,
1165 printMethod);
1166 ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, contentRebased,
1167 printMethod);
1168 ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, contentRebased,
1169 printMethod);
1170 };
1171 ma->forEachObjCClass(diag, contentRebased, printClass);
1172 ma->forEachObjCCategory(diag, contentRebased, printCategory);
1173 ma->forEachObjCProtocol(diag, contentRebased, printProtocol);
1174 }
1175
1176 static void usage()
1177 {
1178 fprintf(stderr, "Usage: dyldinfo [-arch <arch>]* <options>* <mach-o file>+\n"
1179 "\t-segments print segments\n"
1180 "\t-dependents print dependent dylibs\n"
1181 "\t-fixups print locations dyld will rebase/bind\n"
1182 "\t-exports print addresses of all symbols this file exports\n"
1183 "\t-objc print objc classes, categories, etc\n"
1184 );
1185 }
1186
1187 static bool inStringVector(const std::vector<const char*>& vect, const char* target)
1188 {
1189 for (const char* str : vect) {
1190 if ( strcmp(str, target) == 0 )
1191 return true;
1192 }
1193 return false;
1194 }
1195
1196
1197 int main(int argc, const char* argv[])
1198 {
1199 if ( argc == 1 ) {
1200 usage();
1201 return 0;
1202 }
1203
1204 std::vector<const char*> files;
1205 std::vector<const char*> cmdLineArchs;
1206 for (int i=1; i < argc; ++i) {
1207 const char* arg = argv[i];
1208 if ( arg[0] == '-' ) {
1209 if ( strcmp(arg, "-arch") == 0 ) {
1210 if ( ++i < argc ) {
1211 cmdLineArchs.push_back(argv[i]);
1212 }
1213 else {
1214 fprintf(stderr, "-arch missing architecture name");
1215 return 1;
1216 }
1217 }
1218 }
1219 else {
1220 files.push_back(arg);
1221 }
1222 }
1223 if ( files.size() == 0 ) {
1224 usage();
1225 return 0;
1226 }
1227
1228 for (const char* path : files) {
1229 Diagnostics diag;
1230 dyld3::closure::FileSystemPhysical fileSystem;
1231 dyld3::closure::LoadedFileInfo info;
1232 char realerPath[MAXPATHLEN];
1233 __block bool printedError = false;
1234 if (!fileSystem.loadFile(path, info, realerPath, ^(const char* format, ...) {
1235 fprintf(stderr, "dyldinfo: ");
1236 va_list list;
1237 va_start(list, format);
1238 vfprintf(stderr, format, list);
1239 va_end(list);
1240 printedError = true;
1241 })) {
1242 if (!printedError )
1243 fprintf(stderr, "dyldinfo: %s: file not found\n", path);
1244 return 1;
1245 }
1246 __block std::vector<const char*> archesForFile;
1247 __block dyld3::Platform platform = dyld3::Platform::unknown;
1248 if ( dyld3::FatFile::isFatFile(info.fileContent) ) {
1249 const dyld3::FatFile* ff = (dyld3::FatFile*)info.fileContent;
1250 ff->forEachSlice(diag, info.fileContentLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
1251 const char* sliceArchName = dyld3::MachOFile::archName(sliceCpuType, sliceCpuSubType);
1252 if ( cmdLineArchs.empty() || inStringVector(cmdLineArchs, sliceArchName) ) {
1253 archesForFile.push_back(sliceArchName);
1254 const dyld3::MachOFile* mf = (dyld3::MachOFile*)sliceStart;
1255 mf->forEachSupportedPlatform(^(dyld3::Platform plat, uint32_t minOS, uint32_t sdk) {
1256 if ( platform == dyld3::Platform::unknown)
1257 platform = plat;
1258 });
1259 }
1260 });
1261 }
1262 else {
1263 const dyld3::MachOFile* mo = (dyld3::MachOFile*)info.fileContent;
1264 if ( mo->isMachO(diag, info.sliceLen) ) {
1265 archesForFile.push_back(mo->archName());
1266 mo->forEachSupportedPlatform(^(dyld3::Platform plat, uint32_t minOS, uint32_t sdk) {
1267 if ( platform == dyld3::Platform::unknown)
1268 platform = plat;
1269 });
1270 }
1271 else {
1272 fprintf(stderr, "dyldinfo: %s: %s\n", path, diag.errorMessage());
1273 return 1;
1274 }
1275 }
1276 if ( archesForFile.empty() ) {
1277 fprintf(stderr, "dyldinfo: %s: does not contain specified arch(s)\n", path);
1278 return 1;
1279 }
1280 char loadedPath[MAXPATHLEN];
1281 for (const char* sliceArch : archesForFile) {
1282 info = dyld3::MachOAnalyzer::load(diag, fileSystem, path, dyld3::GradedArchs::forName(sliceArch), platform, loadedPath);
1283 if ( diag.hasError() ) {
1284 fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
1285 return 1;
1286 }
1287 const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)info.fileContent;
1288 printf("%s [%s]:\n", path, sliceArch);
1289
1290 bool somethingPrinted = false;
1291 for (int i=1; i < argc; ++i) {
1292 const char* arg = argv[i];
1293 if ( arg[0] != '-' )
1294 continue;
1295 if ( strcmp(arg, "-arch") == 0 ) {
1296 // handled previously
1297 ++i;
1298 }
1299 else if ( strcmp(arg, "-platform") == 0 ) {
1300 printPlatforms(ma);
1301 somethingPrinted = true;
1302 }
1303 else if ( strcmp(arg, "-segments") == 0 ) {
1304 printSegments(ma);
1305 somethingPrinted = true;
1306 }
1307 else if ( strcmp(arg, "-dependents") == 0 ) {
1308 printDependents(ma);
1309 somethingPrinted = true;
1310 }
1311 else if ( strcmp(arg, "-fixups") == 0 ) {
1312 printFixups(ma);
1313 somethingPrinted = true;
1314 }
1315 else if ( strcmp(arg, "-exports") == 0 ) {
1316 printExports(ma);
1317 somethingPrinted = true;
1318 }
1319 else if ( strcmp(arg, "-fixup_chains") == 0 ) {
1320 printChains(ma);
1321 somethingPrinted = true;
1322 }
1323 else if ( strcmp(arg, "-fixup_chain_details") == 0 ) {
1324 printChainDetails(ma);
1325 somethingPrinted = true;
1326 }
1327 else if ( strcmp(arg, "-symbolic_fixups") == 0 ) {
1328 printSymbolicFixups(ma);
1329 somethingPrinted = true;
1330 }
1331 else if ( strcmp(arg, "-opcodes") == 0 ) {
1332 }
1333 else if ( strcmp(arg, "-shared_region") == 0 ) {
1334 }
1335 else if ( strcmp(arg, "-function_starts") == 0 ) {
1336 }
1337 else if ( strcmp(arg, "-data_in_code") == 0 ) {
1338 }
1339 else if ( strcmp(arg, "-objc") == 0 ) {
1340 printObjC(ma);
1341 somethingPrinted = true;
1342 }
1343 else {
1344 fprintf(stderr, "unknown option: %s\n", arg);
1345 return 1;
1346 }
1347 }
1348 if ( !somethingPrinted ) {
1349 printPlatforms(ma);
1350 printSegments(ma);
1351 printDependents(ma);
1352 printFixups(ma);
1353 printExports(ma);
1354 printObjC(ma);
1355 }
1356
1357 }
1358 }
1359
1360 return 0;
1361 }
1362
1363
1364