dyld-832.7.1.tar.gz
[apple/dyld.git] / dyld3 / ClosurePrinter.cpp
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <string.h>
25
26 #include <string>
27 #include <map>
28 #include <vector>
29
30 #include "ClosurePrinter.h"
31 #include "JSONWriter.h"
32
33 #include "objc-shared-cache.h"
34
35 using namespace dyld3::json;
36
37 namespace dyld3 {
38 namespace closure {
39
40 static std::string printTarget(const Array<const ImageArray*>& imagesArrays, Image::ResolvedSymbolTarget target)
41 {
42 const Image* targetImage;
43 uint64_t value;
44 switch ( target.image.kind ) {
45 case Image::ResolvedSymbolTarget::kindImage:
46 targetImage = ImageArray::findImage(imagesArrays, target.image.imageNum);
47 if ( target.image.offset & 0x8000000000ULL ) {
48 uint64_t signExtend = target.image.offset | 0xFFFFFF0000000000ULL;
49 return std::string("bind to ") + targetImage->leafName() + " - " + hex8(-signExtend);
50 }
51 else
52 return std::string("bind to ") + hex4(target.image.imageNum) + "-" + targetImage->leafName() + " + " + hex8(target.image.offset);
53 break;
54 case Image::ResolvedSymbolTarget::kindSharedCache:
55 return std::string("bind to dyld cache + ") + hex8(target.sharedCache.offset);
56 break;
57 case Image::ResolvedSymbolTarget::kindAbsolute:
58 value = target.absolute.value;
59 if ( value & 0x2000000000000000LL )
60 value |= 0xC000000000000000LL;
61 return std::string("bind to absolute ") + hex(value);
62 break;
63 }
64 return "???";
65 }
66
67 static const char* nameForType(TypedBytes::Type type) {
68 switch (type) {
69 // containers
70 case TypedBytes::Type::launchClosure:
71 return "launchClosure";
72 case TypedBytes::Type::imageArray:
73 return "imageArray";
74 case TypedBytes::Type::image:
75 return "image";
76 case TypedBytes::Type::dlopenClosure:
77 return "dlopenClosure";
78 // attributes for Images
79 case TypedBytes::Type::imageFlags:
80 return "imageFlags";
81 case TypedBytes::Type::pathWithHash:
82 return "pathWithHash";
83 case TypedBytes::Type::fileInodeAndTime:
84 return "fileInodeAndTime";
85 case TypedBytes::Type::cdHash:
86 return "cdHash";
87 case TypedBytes::Type::uuid:
88 return "uuid";
89 case TypedBytes::Type::mappingInfo:
90 return "mappingInfo";
91 case TypedBytes::Type::diskSegment:
92 return "diskSegment";
93 case TypedBytes::Type::cacheSegment:
94 return "cacheSegment";
95 case TypedBytes::Type::dependents:
96 return "dependents";
97 case TypedBytes::Type::initOffsets:
98 return "initOffsets";
99 case TypedBytes::Type::dofOffsets:
100 return "dofOffsets";
101 case TypedBytes::Type::codeSignLoc:
102 return "codeSignLoc";
103 case TypedBytes::Type::fairPlayLoc:
104 return "fairPlayLoc";
105 case TypedBytes::Type::rebaseFixups:
106 return "rebaseFixups";
107 case TypedBytes::Type::bindFixups:
108 return "bindFixups";
109 case TypedBytes::Type::cachePatchInfo:
110 return "cachePatchInfo";
111 case TypedBytes::Type::textFixups:
112 return "textFixups";
113 case TypedBytes::Type::imageOverride:
114 return "imageOverride";
115 case TypedBytes::Type::initBefores:
116 return "initBefores";
117 case TypedBytes::Type::initsSection:
118 return "initSection";
119 case TypedBytes::Type::chainedFixupsTargets:
120 return "chainedFixupsTargets";
121 case TypedBytes::Type::termOffsets:
122 return "termOffsets";
123 case TypedBytes::Type::chainedStartsOffset:
124 return "chainedStartsOffset";
125 case TypedBytes::Type::objcFixups:
126 return "objcFixups";
127 // attributes for Closures (launch or dlopen)
128 case TypedBytes::Type::closureFlags:
129 return "closureFlags";
130 case TypedBytes::Type::dyldCacheUUID:
131 return "dyldCacheUUID";
132 case TypedBytes::Type::missingFiles:
133 return "missingFiles";
134 case TypedBytes::Type::envVar:
135 return "envVar";
136 case TypedBytes::Type::topImage:
137 return "topImage";
138 case TypedBytes::Type::libDyldEntry:
139 return "libDyldEntry";
140 case TypedBytes::Type::libSystemNum:
141 return "libSystemNum";
142 case TypedBytes::Type::mainEntry:
143 return "mainEntry";
144 case TypedBytes::Type::startEntry:
145 return "startEntry";
146 case TypedBytes::Type::cacheOverrides:
147 return "cacheOverrides";
148 case TypedBytes::Type::interposeTuples:
149 return "interposeTuples";
150 case TypedBytes::Type::existingFiles:
151 return "existingFiles";
152 case TypedBytes::Type::selectorTable:
153 return "selectorTable";
154 case TypedBytes::Type::classTable:
155 return "classTable";
156 case TypedBytes::Type::warning:
157 return "warning";
158 case TypedBytes::Type::duplicateClassesTable:
159 return "duplicateClassesTable";
160 case TypedBytes::Type::progVars:
161 return "programVars";
162 }
163 }
164
165 static Node buildImageNode(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups,
166 bool printDependentsDetails, bool printRaw,
167 const DyldSharedCache* dyldCache, const closure::ObjCSelectorOpt* selOpt,
168 const Array<closure::Image::ObjCSelectorImage>& selImages)
169 {
170 __block Node imageNode;
171
172 if ( image->isInvalid() )
173 return imageNode;
174
175 imageNode.map["image-num"].value = hex4(image->imageNum());
176 imageNode.map["path"].value = image->path();
177
178 if (printRaw) {
179 __block Node attributes;
180 image->forEachAttribute(^(const TypedBytes *typedBytes, bool &stop) {
181 Node anAttribute;
182 anAttribute.map["type"].value = decimal((uint32_t)typedBytes->type);
183 anAttribute.map["type-name"].value = nameForType((TypedBytes::Type)typedBytes->type);
184 anAttribute.map["length"].value = decimal(typedBytes->payloadLength);
185 attributes.array.push_back(anAttribute);
186 });
187 imageNode.map["attributes"] = attributes;
188 return imageNode;
189 }
190
191 __block Node imageAliases;
192 image->forEachAlias(^(const char* aliasPath, bool& stop) {
193 Node anAlias;
194 anAlias.value = aliasPath;
195 imageAliases.array.push_back(anAlias);
196 });
197 if ( !imageAliases.array.empty() )
198 imageNode.map["aliases"] = imageAliases;
199 uuid_t uuid;
200 if ( image->getUuid(uuid) ) {
201 uuid_string_t uuidStr;
202 uuid_unparse(uuid, uuidStr);
203 imageNode.map["uuid"].value = uuidStr;
204 }
205 imageNode.map["has-objc"].value = (image->hasObjC() ? "true" : "false");
206 imageNode.map["has-weak-defs"].value = (image->hasWeakDefs() ? "true" : "false");
207 imageNode.map["has-plus-loads"].value = (image->mayHavePlusLoads() ? "true" : "false");
208 imageNode.map["never-unload"].value = (image->neverUnload() ? "true" : "false");
209 imageNode.map["has-precomputed-objc"].value = (image->hasPrecomputedObjC() ? "true" : "false");
210 // if ( image->cwdMustBeThisDir() )
211 // imageNode.map["cwd-must-be-this-dir"].value = "true";
212 if ( !image->inDyldCache() ) {
213 uint32_t csFileOffset;
214 uint32_t csSize;
215 if ( image->hasCodeSignature(csFileOffset, csSize) ) {
216 imageNode.map["code-sign-location"].map["offset"].value = hex(csFileOffset);
217 imageNode.map["code-sign-location"].map["size"].value = hex(csSize);
218 }
219 // uint32_t fpTextOffset;
220 // uint32_t fpSize;
221 // if ( image->isFairPlayEncrypted(fpTextOffset, fpSize) ) {
222 // imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset);
223 // imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize);
224 // }
225 uint64_t inode;
226 uint64_t mTime;
227 if ( image->hasFileModTimeAndInode(inode, mTime) ) {
228 imageNode.map["file-mod-time"].value = hex(inode);
229 imageNode.map["file-inode"].value = hex(mTime);
230 }
231 image->forEachCDHash(^(const uint8_t *cdHash, bool& stop) {
232 std::string cdHashStr;
233 cdHashStr.reserve(24);
234 for (int i=0; i < 20; ++i) {
235 uint8_t byte = cdHash[i];
236 uint8_t nibbleL = byte & 0x0F;
237 uint8_t nibbleH = byte >> 4;
238 if ( nibbleH < 10 )
239 cdHashStr += '0' + nibbleH;
240 else
241 cdHashStr += 'a' + (nibbleH-10);
242 if ( nibbleL < 10 )
243 cdHashStr += '0' + nibbleL;
244 else
245 cdHashStr += 'a' + (nibbleL-10);
246 }
247 if ( cdHashStr != "0000000000000000000000000000000000000000" ) {
248 Node hashNode;
249 hashNode.value = cdHashStr;
250 imageNode.map["cd-hashes"].array.push_back(hashNode);
251 }
252 });
253 imageNode.map["total-vm-size"].value = hex(image->vmSizeToMap());
254 uint64_t sliceOffset = image->sliceOffsetInFile();
255 if ( sliceOffset != 0 )
256 imageNode.map["file-offset-of-slice"].value = hex(sliceOffset);
257 //if ( image->hasTextRelocs() )
258 // imageNode.map["has-text-relocs"].value = "true";
259 image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& stop) {
260 Node segInfoNode;
261 segInfoNode.map["file-offset"].value = hex(fileOffset);
262 segInfoNode.map["file-size"].value = hex(fileSize);
263 segInfoNode.map["vm-size"].value = hex(vmSize);
264 imageNode.map["mappings"].array.push_back(segInfoNode);
265 switch ( permissions ) {
266 case 0:
267 segInfoNode.map["permissions"].value = "--";
268 break;
269 case 1:
270 segInfoNode.map["permissions"].value = "r";
271 break;
272 case 2:
273 segInfoNode.map["permissions"].value = "ro"; // r/w then r/o
274 break;
275 case 3:
276 segInfoNode.map["permissions"].value = "rw";
277 break;
278 case 4:
279 segInfoNode.map["permissions"].value = "rx";
280 break;
281 default:
282 segInfoNode.map["permissions"].value = "??";
283 }
284 });
285
286 uint64_t expectedInode;
287 uint64_t expectedMtime;
288 if ( image->hasFileModTimeAndInode(expectedInode, expectedMtime) ) {
289 imageNode.map["file-inode"].value = hex(expectedInode);
290 imageNode.map["file-mod-time"].value = hex(expectedMtime);
291 }
292
293 if ( printFixups ) {
294 image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
295 // rebase
296 imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "rebase";
297 }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
298 // bind
299 imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
300 }, ^(uint64_t startsStructImageOffset, const Array<Image::ResolvedSymbolTarget>& targets, bool& stop) {
301 // chain
302 imageNode.map["fixups-chain-starts-offset"].value = hex8(startsStructImageOffset);
303 for (const Image::ResolvedSymbolTarget& target: targets) {
304 Node targetNode;
305 targetNode.value = printTarget(imagesArrays, target);
306 imageNode.map["fixups-targets"].array.push_back(targetNode);
307 }
308
309 }, ^(uint64_t imageOffsetToFixup) {
310 // fixupObjCImageInfo
311 imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc image info pre-optimized by dyld flag";
312 }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
313 // fixupObjCProtocol
314 imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
315 }, ^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) {
316 // fixupObjCSelRefs
317 Image::ResolvedSymbolTarget target;
318 if ( inSharedCache ) {
319 const char* selectorString = dyldCache->objcOpt()->selopt()->getEntryForIndex(selectorIndex);
320 target.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache;
321 target.sharedCache.offset = (uint64_t)selectorString - (uint64_t)dyldCache;
322 } else {
323 ImageNum imageNum;
324 uint64_t vmOffset;
325 bool gotLocation = selOpt->getStringLocation(selectorIndex, selImages, imageNum, vmOffset);
326 assert(gotLocation);
327 target.image.kind = Image::ResolvedSymbolTarget::kindImage;
328 target.image.imageNum = imageNum;
329 target.image.offset = vmOffset;
330 }
331 imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = printTarget(imagesArrays, target);
332 }, ^(uint64_t imageOffsetToFixup, bool &stop) {
333 // fixupObjCStableSwift
334 imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc set stable Swift";
335 }, ^(uint64_t imageOffsetToFixup, bool &stop) {
336 // fixupObjCMethodList
337 imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc set fixed up method list";
338 });
339 image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool &stop) {
340 // rebase
341 imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "text rebase";
342 }, ^(uint32_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
343 imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = "text " + printTarget(imagesArrays, target);
344 });
345 }
346 }
347 else {
348 if ( printFixups ) {
349 if ( dyldCache != nullptr ) {
350 uint32_t imageIndex = image->imageNum() - (uint32_t)dyldCache->cachedDylibsImageArray()->startImageNum();
351 dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* name) {
352 __block Node implNode;
353 implNode.map["name"].value = name;
354 implNode.map["impl-cache-offset"].value = hex8(cacheOffsetOfImpl);
355 dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) {
356 Node siteNode;
357 siteNode.map["cache-offset"].value = hex8(patchLocation.cacheOffset);
358 if ( patchLocation.addend != 0 )
359 siteNode.map["addend"].value = hex(patchLocation.addend);
360 if ( patchLocation.authenticated != 0 ) {
361 siteNode.map["key"].value = DyldSharedCache::keyName(patchLocation);
362 siteNode.map["address-diversity"].value = patchLocation.usesAddressDiversity ? "true" : "false";
363 siteNode.map["discriminator"].value = hex4(patchLocation.discriminator);
364 }
365 implNode.map["usage-sites"].array.push_back(siteNode);
366 });
367 imageNode.map["patches"].array.push_back(implNode);
368 });
369 }
370 }
371 }
372
373 // add dependents
374 image->forEachDependentImage(^(uint32_t depIndex, Image::LinkKind kind, ImageNum imageNum, bool& stop) {
375 Node depMapNode;
376 const Image* depImage = ImageArray::findImage(imagesArrays, imageNum);
377 depMapNode.map["image-num"].value = hex4(imageNum);
378 if ( depImage != nullptr )
379 depMapNode.map["path"].value = depImage->path();
380 switch ( kind ) {
381 case Image::LinkKind::regular:
382 depMapNode.map["link"].value = "regular";
383 break;
384 case Image::LinkKind::reExport:
385 depMapNode.map["link"].value = "re-export";
386 break;
387 case Image::LinkKind::upward:
388 depMapNode.map["link"].value = "upward";
389 break;
390 case Image::LinkKind::weak:
391 depMapNode.map["link"].value = "weak";
392 break;
393 }
394 imageNode.map["dependents"].array.push_back(depMapNode);
395 });
396
397 // add initializers
398 bool usesInitsSection = image->forEachInitializerSection(^(uint32_t sectionOffset, uint32_t sectionSize) {
399 Node initSectNode;
400 initSectNode.map["offset"].value = hex(sectionOffset);
401 initSectNode.map["size"].value = hex(sectionSize);
402 imageNode.map["initializers-section"].array.push_back(initSectNode);
403 });
404 if ( !usesInitsSection ) {
405 image->forEachInitializer(nullptr, ^(const void* initializer) {
406 Node initNode;
407 initNode.value = hex((long)initializer);
408 imageNode.map["initializer-offsets"].array.push_back(initNode);
409 });
410 }
411 __block Node initBeforeNode;
412 image->forEachImageToInitBefore(^(ImageNum imageToInit, bool& stop) {
413 Node beforeNode;
414 const Image* initImage = ImageArray::findImage(imagesArrays, imageToInit);
415 assert(initImage != nullptr);
416 beforeNode.value = initImage->path();
417 imageNode.map["initializer-order"].array.push_back(beforeNode);
418 });
419
420 // add static terminators
421 image->forEachTerminator(nullptr, ^(const void* terminator) {
422 Node termNode;
423 termNode.value = hex8((long)terminator);
424 imageNode.map["terminator-offsets"].array.push_back(termNode);
425 });
426
427 ImageNum cacheImageNum;
428 if ( image->isOverrideOfDyldCacheImage(cacheImageNum) ) {
429 imageNode.map["override-of-dyld-cache-image"].value = ImageArray::findImage(imagesArrays, cacheImageNum)->path();
430 }
431
432 if ( image->inDyldCache() && image->overridableDylib() ) {
433 imageNode.map["overridable-dylib"].value = "true";
434 }
435
436
437 #if 0
438 // add things to init before this image
439 __block Node initBeforeNode;
440 image->forEachInitBefore(groupList, ^(Image beforeImage) {
441 Node beforeNode;
442 beforeNode.value = beforeimage->path();
443 imageNode.map["initializer-order"].array.push_back(beforeNode);
444 });
445
446 // add override info if relevant
447 group.forEachImageRefOverride(groupList, ^(Image standardDylib, Image overrideDylib, bool& stop) {
448 if ( overrideDylib.binaryData() == image->binaryData() ) {
449 imageNode.map["override-of-cached-dylib"].value = standardDylib.path();
450 }
451 });
452 // add dtrace info
453 image->forEachDOF(nullptr, ^(const void* section) {
454 Node initNode;
455 initNode.value = hex((long)section);
456 imageNode.map["dof-offsets"].array.push_back(initNode);
457 });
458 #endif
459
460 return imageNode;
461 }
462
463
464 static Node buildImageArrayNode(const ImageArray* imageArray, const Array<const ImageArray*>& imagesArrays,
465 bool printFixups, bool printDependentsDetails, bool printRaw,
466 const DyldSharedCache* dyldCache, const closure::ObjCSelectorOpt* selOpt,
467 const Array<closure::Image::ObjCSelectorImage>& selImages)
468 {
469 __block Node images;
470 imageArray->forEachImage(^(const Image* image, bool& stop) {
471 images.array.push_back(buildImageNode(image, imagesArrays, printFixups, printDependentsDetails, printRaw, dyldCache, selOpt, selImages));
472 });
473 return images;
474 }
475
476
477 static Node buildClosureNode(const DlopenClosure* closure, const Array<const ImageArray*>& imagesArrays,
478 bool printFixups, bool printRaw, bool printDependentsDetails)
479 {
480 __block Node root;
481 root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays,
482 printFixups, printDependentsDetails, printRaw,
483 nullptr, nullptr, {});
484
485 closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
486 Node patchNode;
487 patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
488 patchNode.map["func-image-num"].value = hex4(patchEntry.overriddenDylibInCache);
489 patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
490 root.map["dyld-cache-fixups"].array.push_back(patchNode);
491 });
492
493 return root;
494 }
495
496 static Node buildClosureNode(const LaunchClosure* closure, const Array<const ImageArray*>& imagesArrays,
497 bool printFixups, bool printDependentsDetails, bool printRaw,
498 const DyldSharedCache* dyldCache)
499 {
500 __block Node root;
501
502 Array<Image::ObjCSelectorImage> selectorImages;
503 const closure::ObjCSelectorOpt* selectorHashTable = nullptr;
504 bool hasPreoptimizedObjCSelectors = closure->selectorHashTable(selectorImages, selectorHashTable);
505
506 root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups,
507 printDependentsDetails, printRaw,
508 dyldCache, selectorHashTable, selectorImages);
509
510 if ( printRaw ) {
511 __block Node attributes;
512 closure->forEachAttribute(^(const TypedBytes *typedBytes, bool &stop) {
513 Node anAttribute;
514 anAttribute.map["type"].value = decimal((uint32_t)typedBytes->type);
515 anAttribute.map["type-name"].value = nameForType((TypedBytes::Type)typedBytes->type);
516 anAttribute.map["length"].value = decimal(typedBytes->payloadLength);
517 attributes.array.push_back(anAttribute);
518 });
519 root.map["attributes"] = attributes;
520 return root;
521 }
522
523 closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
524 Node patchNode;
525 patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
526 patchNode.map["func-image-num"].value = hex4(patchEntry.overriddenDylibInCache);
527 patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
528 root.map["dyld-cache-fixups"].array.push_back(patchNode);
529 });
530
531 Image::ResolvedSymbolTarget entry;
532 if ( closure->mainEntry(entry) )
533 root.map["main"].value = printTarget(imagesArrays, entry);
534 else if ( closure->startEntry(entry) )
535 root.map["start"].value = printTarget(imagesArrays, entry);
536
537 Image::ResolvedSymbolTarget libdyldEntry;
538 closure->libDyldEntry(libdyldEntry);
539 root.map["libdyld-entry"].value = printTarget(imagesArrays, libdyldEntry);
540
541 root.map["uses-@paths"].value = (closure->usedAtPaths() ? "true" : "false");
542 root.map["uses-fallback-paths"].value = (closure->usedFallbackPaths() ? "true" : "false");
543
544 // add missing files array if they exist
545 closure->forEachMustBeMissingFile(^(const char* path, bool& stop) {
546 Node fileNode;
547 fileNode.value = path;
548 root.map["must-be-missing-files"].array.push_back(fileNode);
549 });
550
551 // add skipped files array if they exist
552 closure->forEachSkipIfExistsFile(^(const LaunchClosure::SkippedFile &file, bool &stop) {
553 Node fileNode;
554 fileNode.map["path"].value = file.path;
555 fileNode.map["file-mod-time"].value = hex(file.mtime);
556 fileNode.map["file-inode"].value = hex(file.inode);
557 root.map["skipped-existing-files"].array.push_back(fileNode);
558 });
559
560 // add interposing info, if any
561 closure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool& stop) {
562 Node tupleNode;
563 tupleNode.map["stock"].value = printTarget(imagesArrays, tuple.stockImplementation);
564 tupleNode.map["replace"].value = printTarget(imagesArrays, tuple.newImplementation);
565 root.map["interposing-tuples"].array.push_back(tupleNode);
566 });
567
568 root.map["initial-image-count"].value = decimal(closure->initialLoadCount());
569
570 // add env-vars if they exist
571 closure->forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
572 const char* equ = strchr(keyEqualValue, '=');
573 if ( equ != nullptr ) {
574 char key[512];
575 strncpy(key, keyEqualValue, equ-keyEqualValue);
576 key[equ-keyEqualValue] = '\0';
577 root.map["env-vars"].map[key].value = equ+1;
578 }
579 });
580
581 if (hasPreoptimizedObjCSelectors) {
582 __block Node selectorsNode;
583 selectorHashTable->forEachString(selectorImages,
584 ^(uint64_t selVMOffset, ImageNum imageNum) {
585 // Convert to a target we can get a real name for
586 dyld3::closure::Image::ResolvedSymbolTarget target;
587 target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
588 target.image.imageNum = imageNum;
589 target.image.offset = selVMOffset;
590
591 Node targetNode;
592 targetNode.value = printTarget(imagesArrays, target);
593 selectorsNode.array.push_back(targetNode);
594 });
595
596 root.map["objc-selectors"] = selectorsNode;
597 }
598
599 Array<Image::ObjCClassImage> classImages;
600 const ObjCClassOpt* classHashTable = nullptr;
601 const ObjCClassOpt* protocolHashTable = nullptr;
602 if (closure->classAndProtocolHashTables(classImages, classHashTable, protocolHashTable)) {
603 if ( classHashTable != nullptr ) {
604 __block Node classesNode;
605
606 classHashTable->forEachClass(classImages,
607 ^(uint64_t classNameVMOffset, ImageNum imageNum) {
608 // Convert to a target we can get a real name for
609 dyld3::closure::Image::ResolvedSymbolTarget target;
610 target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
611 target.image.imageNum = imageNum;
612 target.image.offset = classNameVMOffset;
613
614 Node targetNode;
615 targetNode.value = printTarget(imagesArrays, target);
616
617 Node classNode;
618 classNode.map["name"] = targetNode;
619 classesNode.array.push_back(classNode);
620 },
621 ^(uint64_t classVMOffset, ImageNum imageNum) {
622 dyld3::closure::Image::ResolvedSymbolTarget implTarget;
623 implTarget.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
624 implTarget.image.imageNum = imageNum;
625 implTarget.image.offset = classVMOffset;
626
627 Node implNode;
628 implNode.value = printTarget(imagesArrays, implTarget);
629 classesNode.array.back().map["implementations"].array.push_back(implNode);
630 });
631
632 root.map["objc-classes"] = classesNode;
633 }
634
635 if ( protocolHashTable != nullptr ) {
636 __block Node protocolsNode;
637
638 protocolHashTable->forEachClass(classImages,
639 ^(uint64_t protocolNameVMOffset, ImageNum imageNum) {
640 // Convert to a target we can get a real name for
641 dyld3::closure::Image::ResolvedSymbolTarget target;
642 target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
643 target.image.imageNum = imageNum;
644 target.image.offset = protocolNameVMOffset;
645
646 Node targetNode;
647 targetNode.value = printTarget(imagesArrays, target);
648
649 Node protocolNode;
650 protocolNode.map["name"] = targetNode;
651 protocolsNode.array.push_back(protocolNode);
652 },
653 ^(uint64_t protocolVMOffset, ImageNum imageNum) {
654 dyld3::closure::Image::ResolvedSymbolTarget implTarget;
655 implTarget.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
656 implTarget.image.imageNum = imageNum;
657 implTarget.image.offset = protocolVMOffset;
658
659 Node implNode;
660 implNode.value = printTarget(imagesArrays, implTarget);
661 protocolsNode.array.back().map["implementations"].array.push_back(implNode);
662 });
663
664 root.map["objc-protocols"] = protocolsNode;
665 }
666 }
667
668 const ObjCClassDuplicatesOpt* duplicateClassesHashTable = nullptr;
669 closure->duplicateClassesHashTable(duplicateClassesHashTable);
670 if ( duplicateClassesHashTable != nullptr ) {
671 __block Node duplicateClassesNode;
672 duplicateClassesHashTable->forEachClass(^(Image::ObjCDuplicateClass duplicateClass) {
673 objc_opt::objc_clsopt_t* clsOpt = dyldCache->objcOpt()->clsopt();
674 const char* className = clsOpt->getClassNameForIndex(duplicateClass.sharedCacheClassOptIndex);
675 const void* classImpl = clsOpt->getClassForIndex(duplicateClass.sharedCacheClassOptIndex, duplicateClass.sharedCacheClassDuplicateIndex);
676
677 // Convert to a target we can get a real name for
678 dyld3::closure::Image::ResolvedSymbolTarget target;
679 target.sharedCache.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache;
680 target.sharedCache.offset = (uint64_t)classImpl - (uint64_t)dyldCache;
681
682 Node targetNode;
683 targetNode.value = printTarget(imagesArrays, target);
684
685 Node duplicateClassNode;
686 duplicateClassNode.map["name"].value = className;
687 duplicateClassNode.map["implementation"] = targetNode;
688 duplicateClassNode.array.push_back(targetNode);
689
690 duplicateClassesNode.array.push_back(duplicateClassNode);
691 });
692
693 root.map["objc-duplicate-classes"] = duplicateClassesNode;
694 }
695
696 // add warnings for objc if they exist
697 closure->forEachWarning(Closure::Warning::duplicateObjCClass, ^(const char *warning, bool &stop) {
698 Node warningNode;
699 warningNode.value = warning;
700 root.map["objc-duplicate-class-warnings"].array.push_back(warningNode);
701 });
702
703 // add program vars info for old macOS binaries
704 uint32_t progVarsOffset;
705 if ( closure->hasProgramVars(progVarsOffset) )
706 root.map["program-vars-offset"].value = hex8(progVarsOffset);
707
708 #if 0
709
710
711 // add uuid of dyld cache this closure requires
712 closure.dyldCacheUUID();
713 uuid_string_t cacheUuidStr;
714 uuid_unparse(*closure.dyldCacheUUID(), cacheUuidStr);
715 root.map["dyld-cache-uuid"].value = cacheUuidStr;
716
717 // add top level images
718 Node& rootImages = root.map["root-images"];
719 uint32_t initImageCount = closure.mainExecutableImageIndex();
720 rootImages.array.resize(initImageCount+1);
721 for (uint32_t i=0; i <= initImageCount; ++i) {
722 const Image image = closure.group().image(i);
723 uuid_string_t uuidStr;
724 uuid_unparse(image->uuid(), uuidStr);
725 rootImages.array[i].value = uuidStr;
726 }
727 root.map["initial-image-count"].value = decimal(closure.initialImageCount());
728
729 // add images
730 root.map["group-num"].value = decimal(closure.group().groupNum());
731
732
733 __block Node cacheOverrides;
734 closure.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop) {
735 Node patch;
736 patch.map["patch-index"].value = decimal(patchTableIndex);
737 patch.map["replacement"].value = "{closure[" + decimal(imageIndexInClosure) + "]+" + hex(imageOffset) + "}";
738 cacheOverrides.array.push_back(patch);
739 });
740 if ( !cacheOverrides.array.empty() )
741 root.map["dyld-cache-overrides"].array = cacheOverrides.array;
742 #endif
743 return root;
744 }
745
746 void printImageAsJSON(const Image* image, const Array<const ImageArray*>& imagesArrays,
747 bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
748 {
749 Node root = buildImageNode(image, imagesArrays, printFixups, false, printRaw, dyldCache, nullptr, {});
750 printJSON(root, 0, out);
751 }
752
753 void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups, bool printRaw, std::ostream& out)
754 {
755 const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
756 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
757 imagesArrays.push_back(dylibs);
758
759 Node root = buildImageArrayNode(dylibs, imagesArrays, printFixups, false, printRaw, dyldCache, nullptr, {});
760 printJSON(root, 0, out);
761 }
762
763 void printClosureAsJSON(const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays,
764 bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
765 {
766 Node root = buildClosureNode(cls, imagesArrays, printFixups, false, printRaw, dyldCache);
767 printJSON(root, 0, out);
768 }
769
770 void printClosureAsJSON(const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays,
771 bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
772 {
773 Node root = buildClosureNode(cls, imagesArrays, printFixups, printRaw, false);
774 printJSON(root, 0, out);
775 }
776
777
778 } // namespace closure
779 } // namespace dyld3