2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
30 #include "ClosurePrinter.h"
31 #include "JSONWriter.h"
33 #include "objc-shared-cache.h"
35 using namespace dyld3::json
;
40 static std::string
printTarget(const Array
<const ImageArray
*>& imagesArrays
, Image::ResolvedSymbolTarget target
)
42 const Image
* targetImage
;
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
);
52 return std::string("bind to ") + targetImage
->leafName() + " + " + hex8(target
.image
.offset
);
54 case Image::ResolvedSymbolTarget::kindSharedCache
:
55 return std::string("bind to dyld cache + ") + hex8(target
.sharedCache
.offset
);
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
);
67 static const char* nameForType(TypedBytes::Type type
) {
70 case TypedBytes::Type::launchClosure
:
71 return "launchClosure";
72 case TypedBytes::Type::imageArray
:
74 case TypedBytes::Type::image
:
76 case TypedBytes::Type::dlopenClosure
:
77 return "dlopenClosure";
78 // attributes for Images
79 case TypedBytes::Type::imageFlags
:
81 case TypedBytes::Type::pathWithHash
:
82 return "pathWithHash";
83 case TypedBytes::Type::fileInodeAndTime
:
84 return "fileInodeAndTime";
85 case TypedBytes::Type::cdHash
:
87 case TypedBytes::Type::uuid
:
89 case TypedBytes::Type::mappingInfo
:
91 case TypedBytes::Type::diskSegment
:
93 case TypedBytes::Type::cacheSegment
:
94 return "cacheSegment";
95 case TypedBytes::Type::dependents
:
97 case TypedBytes::Type::initOffsets
:
99 case TypedBytes::Type::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
:
109 case TypedBytes::Type::cachePatchInfo
:
110 return "cachePatchInfo";
111 case TypedBytes::Type::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
:
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
:
136 case TypedBytes::Type::topImage
:
138 case TypedBytes::Type::libDyldEntry
:
139 return "libDyldEntry";
140 case TypedBytes::Type::libSystemNum
:
141 return "libSystemNum";
142 case TypedBytes::Type::bootUUID
:
144 case TypedBytes::Type::mainEntry
:
146 case TypedBytes::Type::startEntry
:
148 case TypedBytes::Type::cacheOverrides
:
149 return "cacheOverrides";
150 case TypedBytes::Type::interposeTuples
:
151 return "interposeTuples";
152 case TypedBytes::Type::existingFiles
:
153 return "existingFiles";
154 case TypedBytes::Type::selectorTable
:
155 return "selectorTable";
156 case TypedBytes::Type::classTable
:
158 case TypedBytes::Type::warning
:
160 case TypedBytes::Type::duplicateClassesTable
:
161 return "duplicateClassesTable";
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
)
170 __block Node imageNode
;
172 if ( image
->isInvalid() )
175 imageNode
.map
["image-num"].value
= hex4(image
->imageNum());
176 imageNode
.map
["path"].value
= image
->path();
179 __block Node attributes
;
180 image
->forEachAttribute(^(const TypedBytes
*typedBytes
, bool &stop
) {
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
);
187 imageNode
.map
["attributes"] = attributes
;
191 __block Node imageAliases
;
192 image
->forEachAlias(^(const char* aliasPath
, bool& stop
) {
194 anAlias
.value
= aliasPath
;
195 imageAliases
.array
.push_back(anAlias
);
197 if ( !imageAliases
.array
.empty() )
198 imageNode
.map
["aliases"] = imageAliases
;
200 if ( image
->getUuid(uuid
) ) {
201 uuid_string_t uuidStr
;
202 uuid_unparse(uuid
, uuidStr
);
203 imageNode
.map
["uuid"].value
= uuidStr
;
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 // imageNode.map["platform-binary"].value = (image->isPlatformBinary() ? "true" : "false");
211 // if ( image->cwdMustBeThisDir() )
212 // imageNode.map["cwd-must-be-this-dir"].value = "true";
213 if ( !image
->inDyldCache() ) {
214 uint32_t csFileOffset
;
216 if ( image
->hasCodeSignature(csFileOffset
, csSize
) ) {
217 imageNode
.map
["code-sign-location"].map
["offset"].value
= hex(csFileOffset
);
218 imageNode
.map
["code-sign-location"].map
["size"].value
= hex(csSize
);
220 // uint32_t fpTextOffset;
222 // if ( image->isFairPlayEncrypted(fpTextOffset, fpSize) ) {
223 // imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset);
224 // imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize);
228 if ( image
->hasFileModTimeAndInode(inode
, mTime
) ) {
229 imageNode
.map
["file-mod-time"].value
= hex(inode
);
230 imageNode
.map
["file-inode"].value
= hex(mTime
);
232 image
->forEachCDHash(^(const uint8_t *cdHash
, bool& stop
) {
233 std::string cdHashStr
;
234 cdHashStr
.reserve(24);
235 for (int i
=0; i
< 20; ++i
) {
236 uint8_t byte
= cdHash
[i
];
237 uint8_t nibbleL
= byte
& 0x0F;
238 uint8_t nibbleH
= byte
>> 4;
240 cdHashStr
+= '0' + nibbleH
;
242 cdHashStr
+= 'a' + (nibbleH
-10);
244 cdHashStr
+= '0' + nibbleL
;
246 cdHashStr
+= 'a' + (nibbleL
-10);
248 if ( cdHashStr
!= "0000000000000000000000000000000000000000" ) {
250 hashNode
.value
= cdHashStr
;
251 imageNode
.map
["cd-hashes"].array
.push_back(hashNode
);
254 imageNode
.map
["total-vm-size"].value
= hex(image
->vmSizeToMap());
255 uint64_t sliceOffset
= image
->sliceOffsetInFile();
256 if ( sliceOffset
!= 0 )
257 imageNode
.map
["file-offset-of-slice"].value
= hex(sliceOffset
);
258 //if ( image->hasTextRelocs() )
259 // imageNode.map["has-text-relocs"].value = "true";
260 image
->forEachDiskSegment(^(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool laterReadOnly
, bool& stop
) {
262 segInfoNode
.map
["file-offset"].value
= hex(fileOffset
);
263 segInfoNode
.map
["file-size"].value
= hex(fileSize
);
264 segInfoNode
.map
["vm-size"].value
= hex(vmSize
);
265 imageNode
.map
["mappings"].array
.push_back(segInfoNode
);
266 switch ( permissions
) {
268 segInfoNode
.map
["permissions"].value
= "--";
271 segInfoNode
.map
["permissions"].value
= "r";
274 segInfoNode
.map
["permissions"].value
= "ro"; // r/w then r/o
277 segInfoNode
.map
["permissions"].value
= "rw";
280 segInfoNode
.map
["permissions"].value
= "rx";
283 segInfoNode
.map
["permissions"].value
= "??";
289 image
->forEachFixup(^(uint64_t imageOffsetToRebase
, bool &stop
) {
291 imageNode
.map
["fixups"].map
[hex8(imageOffsetToRebase
)].value
= "rebase";
292 }, ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
294 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= printTarget(imagesArrays
, target
);
295 }, ^(uint64_t startsStructImageOffset
, const Array
<Image::ResolvedSymbolTarget
>& targets
, bool& stop
) {
297 imageNode
.map
["fixups-chain-starts-offset"].value
= hex8(startsStructImageOffset
);
298 for (const Image::ResolvedSymbolTarget
& target
: targets
) {
300 targetNode
.value
= printTarget(imagesArrays
, target
);
301 imageNode
.map
["fixups-targets"].array
.push_back(targetNode
);
304 }, ^(uint64_t imageOffsetToFixup
) {
305 // fixupObjCImageInfo
306 imageNode
.map
["fixups"].map
[hex8(imageOffsetToFixup
)].value
= "objc image info pre-optimized by dyld flag";
307 }, ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
309 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= printTarget(imagesArrays
, target
);
310 }, ^(uint64_t imageOffsetToFixup
, uint32_t selectorIndex
, bool inSharedCache
, bool &stop
) {
312 Image::ResolvedSymbolTarget target
;
313 if ( inSharedCache
) {
314 const char* selectorString
= dyldCache
->objcOpt()->selopt()->getEntryForIndex(selectorIndex
);
315 target
.sharedCache
.kind
= Image::ResolvedSymbolTarget::kindSharedCache
;
316 target
.sharedCache
.offset
= (uint64_t)selectorString
- (uint64_t)dyldCache
;
320 bool gotLocation
= selOpt
->getStringLocation(selectorIndex
, selImages
, imageNum
, vmOffset
);
322 target
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
323 target
.image
.imageNum
= imageNum
;
324 target
.image
.offset
= vmOffset
;
326 imageNode
.map
["fixups"].map
[hex8(imageOffsetToFixup
)].value
= printTarget(imagesArrays
, target
);
327 }, ^(uint64_t imageOffsetToFixup
, bool &stop
) {
328 // fixupObjCStableSwift
329 imageNode
.map
["fixups"].map
[hex8(imageOffsetToFixup
)].value
= "objc set stable Swift";
330 }, ^(uint64_t imageOffsetToFixup
, bool &stop
) {
331 // fixupObjCMethodList
332 imageNode
.map
["fixups"].map
[hex8(imageOffsetToFixup
)].value
= "objc set fixed up method list";
334 image
->forEachTextReloc(^(uint32_t imageOffsetToRebase
, bool &stop
) {
336 imageNode
.map
["fixups"].map
[hex8(imageOffsetToRebase
)].value
= "text rebase";
337 }, ^(uint32_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
338 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= "text " + printTarget(imagesArrays
, target
);
344 if ( dyldCache
!= nullptr ) {
345 uint32_t imageIndex
= image
->imageNum() - (uint32_t)dyldCache
->cachedDylibsImageArray()->startImageNum();
346 dyldCache
->forEachPatchableExport(imageIndex
, ^(uint32_t cacheOffsetOfImpl
, const char* name
) {
347 __block Node implNode
;
348 implNode
.map
["name"].value
= name
;
349 implNode
.map
["impl-cache-offset"].value
= hex8(cacheOffsetOfImpl
);
350 dyldCache
->forEachPatchableUseOfExport(imageIndex
, cacheOffsetOfImpl
, ^(dyld_cache_patchable_location patchLocation
) {
352 siteNode
.map
["cache-offset"].value
= hex8(patchLocation
.cacheOffset
);
353 if ( patchLocation
.addend
!= 0 )
354 siteNode
.map
["addend"].value
= hex(patchLocation
.addend
);
355 if ( patchLocation
.authenticated
!= 0 ) {
356 siteNode
.map
["key"].value
= DyldSharedCache::keyName(patchLocation
);
357 siteNode
.map
["address-diversity"].value
= patchLocation
.usesAddressDiversity
? "true" : "false";
358 siteNode
.map
["discriminator"].value
= hex4(patchLocation
.discriminator
);
360 implNode
.map
["usage-sites"].array
.push_back(siteNode
);
362 imageNode
.map
["patches"].array
.push_back(implNode
);
369 image
->forEachDependentImage(^(uint32_t depIndex
, Image::LinkKind kind
, ImageNum imageNum
, bool& stop
) {
371 const Image
* depImage
= ImageArray::findImage(imagesArrays
, imageNum
);
372 depMapNode
.map
["image-num"].value
= hex4(imageNum
);
373 if ( depImage
!= nullptr )
374 depMapNode
.map
["path"].value
= depImage
->path();
376 case Image::LinkKind::regular
:
377 depMapNode
.map
["link"].value
= "regular";
379 case Image::LinkKind::reExport
:
380 depMapNode
.map
["link"].value
= "re-export";
382 case Image::LinkKind::upward
:
383 depMapNode
.map
["link"].value
= "upward";
385 case Image::LinkKind::weak
:
386 depMapNode
.map
["link"].value
= "weak";
389 imageNode
.map
["dependents"].array
.push_back(depMapNode
);
393 bool usesInitsSection
= image
->forEachInitializerSection(^(uint32_t sectionOffset
, uint32_t sectionSize
) {
395 initSectNode
.map
["offset"].value
= hex(sectionOffset
);
396 initSectNode
.map
["size"].value
= hex(sectionSize
);
397 imageNode
.map
["initializers-section"].array
.push_back(initSectNode
);
399 if ( !usesInitsSection
) {
400 image
->forEachInitializer(nullptr, ^(const void* initializer
) {
402 initNode
.value
= hex((long)initializer
);
403 imageNode
.map
["initializer-offsets"].array
.push_back(initNode
);
406 __block Node initBeforeNode
;
407 image
->forEachImageToInitBefore(^(ImageNum imageToInit
, bool& stop
) {
409 const Image
* initImage
= ImageArray::findImage(imagesArrays
, imageToInit
);
410 assert(initImage
!= nullptr);
411 beforeNode
.value
= initImage
->path();
412 imageNode
.map
["initializer-order"].array
.push_back(beforeNode
);
415 // add static terminators
416 image
->forEachTerminator(nullptr, ^(const void* terminator
) {
418 termNode
.value
= hex8((long)terminator
);
419 imageNode
.map
["terminator-offsets"].array
.push_back(termNode
);
422 ImageNum cacheImageNum
;
423 if ( image
->isOverrideOfDyldCacheImage(cacheImageNum
) ) {
424 imageNode
.map
["override-of-dyld-cache-image"].value
= ImageArray::findImage(imagesArrays
, cacheImageNum
)->path();
429 // add things to init before this image
430 __block Node initBeforeNode
;
431 image
->forEachInitBefore(groupList
, ^(Image beforeImage
) {
433 beforeNode
.value
= beforeimage
->path();
434 imageNode
.map
["initializer-order"].array
.push_back(beforeNode
);
437 // add override info if relevant
438 group
.forEachImageRefOverride(groupList
, ^(Image standardDylib
, Image overrideDylib
, bool& stop
) {
439 if ( overrideDylib
.binaryData() == image
->binaryData() ) {
440 imageNode
.map
["override-of-cached-dylib"].value
= standardDylib
.path();
444 image
->forEachDOF(nullptr, ^(const void* section
) {
446 initNode
.value
= hex((long)section
);
447 imageNode
.map
["dof-offsets"].array
.push_back(initNode
);
455 static Node
buildImageArrayNode(const ImageArray
* imageArray
, const Array
<const ImageArray
*>& imagesArrays
,
456 bool printFixups
, bool printDependentsDetails
, bool printRaw
,
457 const DyldSharedCache
* dyldCache
, const closure::ObjCSelectorOpt
* selOpt
,
458 const Array
<closure::Image::ObjCSelectorImage
>& selImages
)
461 imageArray
->forEachImage(^(const Image
* image
, bool& stop
) {
462 images
.array
.push_back(buildImageNode(image
, imagesArrays
, printFixups
, printDependentsDetails
, printRaw
, dyldCache
, selOpt
, selImages
));
468 static Node
buildClosureNode(const DlopenClosure
* closure
, const Array
<const ImageArray
*>& imagesArrays
,
469 bool printFixups
, bool printRaw
, bool printDependentsDetails
)
472 root
.map
["images"] = buildImageArrayNode(closure
->images(), imagesArrays
,
473 printFixups
, printDependentsDetails
, printRaw
,
474 nullptr, nullptr, {});
476 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
478 patchNode
.map
["func-dyld-cache-offset"].value
= hex8(patchEntry
.exportCacheOffset
);
479 patchNode
.map
["func-image-num"].value
= hex8(patchEntry
.overriddenDylibInCache
);
480 patchNode
.map
["replacement"].value
= printTarget(imagesArrays
, patchEntry
.replacement
);
481 root
.map
["dyld-cache-fixups"].array
.push_back(patchNode
);
487 static Node
buildClosureNode(const LaunchClosure
* closure
, const Array
<const ImageArray
*>& imagesArrays
,
488 bool printFixups
, bool printDependentsDetails
, bool printRaw
,
489 const DyldSharedCache
* dyldCache
)
493 Array
<Image::ObjCSelectorImage
> selectorImages
;
494 const closure::ObjCSelectorOpt
* selectorHashTable
= nullptr;
495 bool hasPreoptimizedObjCSelectors
= closure
->selectorHashTable(selectorImages
, selectorHashTable
);
497 root
.map
["images"] = buildImageArrayNode(closure
->images(), imagesArrays
, printFixups
,
498 printDependentsDetails
, printRaw
,
499 dyldCache
, selectorHashTable
, selectorImages
);
502 __block Node attributes
;
503 closure
->forEachAttribute(^(const TypedBytes
*typedBytes
, bool &stop
) {
505 anAttribute
.map
["type"].value
= decimal((uint32_t)typedBytes
->type
);
506 anAttribute
.map
["type-name"].value
= nameForType((TypedBytes::Type
)typedBytes
->type
);
507 anAttribute
.map
["length"].value
= decimal(typedBytes
->payloadLength
);
508 attributes
.array
.push_back(anAttribute
);
510 root
.map
["attributes"] = attributes
;
514 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
516 patchNode
.map
["func-dyld-cache-offset"].value
= hex8(patchEntry
.exportCacheOffset
);
517 patchNode
.map
["func-image-num"].value
= hex8(patchEntry
.overriddenDylibInCache
);
518 patchNode
.map
["replacement"].value
= printTarget(imagesArrays
, patchEntry
.replacement
);
519 root
.map
["dyld-cache-fixups"].array
.push_back(patchNode
);
522 Image::ResolvedSymbolTarget entry
;
523 if ( closure
->mainEntry(entry
) )
524 root
.map
["main"].value
= printTarget(imagesArrays
, entry
);
525 else if ( closure
->startEntry(entry
) )
526 root
.map
["start"].value
= printTarget(imagesArrays
, entry
);
528 Image::ResolvedSymbolTarget libdyldEntry
;
529 closure
->libDyldEntry(libdyldEntry
);
530 root
.map
["libdyld-entry"].value
= printTarget(imagesArrays
, libdyldEntry
);
532 root
.map
["uses-@paths"].value
= (closure
->usedAtPaths() ? "true" : "false");
533 root
.map
["uses-fallback-paths"].value
= (closure
->usedFallbackPaths() ? "true" : "false");
535 // add missing files array if they exist
536 closure
->forEachMustBeMissingFile(^(const char* path
, bool& stop
) {
538 fileNode
.value
= path
;
539 root
.map
["must-be-missing-files"].array
.push_back(fileNode
);
542 // add skipped files array if they exist
543 closure
->forEachSkipIfExistsFile(^(const LaunchClosure::SkippedFile
&file
, bool &stop
) {
545 fileNode
.map
["path"].value
= file
.path
;
546 fileNode
.map
["file-mod-time"].value
= hex(file
.mtime
);
547 fileNode
.map
["file-inode"].value
= hex(file
.inode
);
548 root
.map
["skipped-existing-files"].array
.push_back(fileNode
);
551 // add interposing info, if any
552 closure
->forEachInterposingTuple(^(const InterposingTuple
& tuple
, bool& stop
) {
554 tupleNode
.map
["stock"].value
= printTarget(imagesArrays
, tuple
.stockImplementation
);
555 tupleNode
.map
["replace"].value
= printTarget(imagesArrays
, tuple
.newImplementation
);
556 root
.map
["interposing-tuples"].array
.push_back(tupleNode
);
559 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
561 patchNode
.map
["func-dyld-cache-offset"].value
= hex8(patchEntry
.exportCacheOffset
);
562 patchNode
.map
["func-image-num"].value
= hex8(patchEntry
.overriddenDylibInCache
);
563 patchNode
.map
["replacement"].value
= printTarget(imagesArrays
, patchEntry
.replacement
);
564 root
.map
["dyld-cache-fixups"].array
.push_back(patchNode
);
567 root
.map
["initial-image-count"].value
= decimal(closure
->initialLoadCount());
569 // add env-vars if they exist
570 closure
->forEachEnvVar(^(const char* keyEqualValue
, bool& stop
) {
571 const char* equ
= strchr(keyEqualValue
, '=');
572 if ( equ
!= nullptr ) {
574 strncpy(key
, keyEqualValue
, equ
-keyEqualValue
);
575 key
[equ
-keyEqualValue
] = '\0';
576 root
.map
["env-vars"].map
[key
].value
= equ
+1;
580 if (hasPreoptimizedObjCSelectors
) {
581 __block Node selectorsNode
;
582 selectorHashTable
->forEachString(selectorImages
,
583 ^(uint64_t selVMOffset
, ImageNum imageNum
) {
584 // Convert to a target we can get a real name for
585 dyld3::closure::Image::ResolvedSymbolTarget target
;
586 target
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
587 target
.image
.imageNum
= imageNum
;
588 target
.image
.offset
= selVMOffset
;
591 targetNode
.value
= printTarget(imagesArrays
, target
);
592 selectorsNode
.array
.push_back(targetNode
);
595 root
.map
["objc-selectors"] = selectorsNode
;
598 Array
<Image::ObjCClassImage
> classImages
;
599 const ObjCClassOpt
* classHashTable
= nullptr;
600 const ObjCClassOpt
* protocolHashTable
= nullptr;
601 if (closure
->classAndProtocolHashTables(classImages
, classHashTable
, protocolHashTable
)) {
602 if ( classHashTable
!= nullptr ) {
603 __block Node classesNode
;
605 classHashTable
->forEachClass(classImages
,
606 ^(uint64_t classNameVMOffset
, ImageNum imageNum
) {
607 // Convert to a target we can get a real name for
608 dyld3::closure::Image::ResolvedSymbolTarget target
;
609 target
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
610 target
.image
.imageNum
= imageNum
;
611 target
.image
.offset
= classNameVMOffset
;
614 targetNode
.value
= printTarget(imagesArrays
, target
);
617 classNode
.map
["name"] = targetNode
;
618 classesNode
.array
.push_back(classNode
);
620 ^(uint64_t classVMOffset
, ImageNum imageNum
) {
621 dyld3::closure::Image::ResolvedSymbolTarget implTarget
;
622 implTarget
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
623 implTarget
.image
.imageNum
= imageNum
;
624 implTarget
.image
.offset
= classVMOffset
;
627 implNode
.value
= printTarget(imagesArrays
, implTarget
);
628 classesNode
.array
.back().map
["implementations"].array
.push_back(implNode
);
631 root
.map
["objc-classes"] = classesNode
;
634 if ( protocolHashTable
!= nullptr ) {
635 __block Node protocolsNode
;
637 protocolHashTable
->forEachClass(classImages
,
638 ^(uint64_t protocolNameVMOffset
, ImageNum imageNum
) {
639 // Convert to a target we can get a real name for
640 dyld3::closure::Image::ResolvedSymbolTarget target
;
641 target
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
642 target
.image
.imageNum
= imageNum
;
643 target
.image
.offset
= protocolNameVMOffset
;
646 targetNode
.value
= printTarget(imagesArrays
, target
);
649 protocolNode
.map
["name"] = targetNode
;
650 protocolsNode
.array
.push_back(protocolNode
);
652 ^(uint64_t protocolVMOffset
, ImageNum imageNum
) {
653 dyld3::closure::Image::ResolvedSymbolTarget implTarget
;
654 implTarget
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
655 implTarget
.image
.imageNum
= imageNum
;
656 implTarget
.image
.offset
= protocolVMOffset
;
659 implNode
.value
= printTarget(imagesArrays
, implTarget
);
660 protocolsNode
.array
.back().map
["implementations"].array
.push_back(implNode
);
663 root
.map
["objc-protocols"] = protocolsNode
;
667 const ObjCClassDuplicatesOpt
* duplicateClassesHashTable
= nullptr;
668 closure
->duplicateClassesHashTable(duplicateClassesHashTable
);
669 if ( duplicateClassesHashTable
!= nullptr ) {
670 __block Node duplicateClassesNode
;
671 duplicateClassesHashTable
->forEachClass(^(Image::ObjCDuplicateClass duplicateClass
) {
672 objc_opt::objc_clsopt_t
* clsOpt
= dyldCache
->objcOpt()->clsopt();
673 const char* className
= clsOpt
->getClassNameForIndex(duplicateClass
.sharedCacheClassOptIndex
);
674 const void* classImpl
= clsOpt
->getClassForIndex(duplicateClass
.sharedCacheClassOptIndex
, duplicateClass
.sharedCacheClassDuplicateIndex
);
676 // Convert to a target we can get a real name for
677 dyld3::closure::Image::ResolvedSymbolTarget target
;
678 target
.sharedCache
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache
;
679 target
.sharedCache
.offset
= (uint64_t)classImpl
- (uint64_t)dyldCache
;
682 targetNode
.value
= printTarget(imagesArrays
, target
);
684 Node duplicateClassNode
;
685 duplicateClassNode
.map
["name"].value
= className
;
686 duplicateClassNode
.map
["implementation"] = targetNode
;
687 duplicateClassNode
.array
.push_back(targetNode
);
689 duplicateClassesNode
.array
.push_back(duplicateClassNode
);
692 root
.map
["objc-duplicate-classes"] = duplicateClassesNode
;
695 // add warnings for objc if they exist
696 closure
->forEachWarning(Closure::Warning::duplicateObjCClass
, ^(const char *warning
, bool &stop
) {
698 warningNode
.value
= warning
;
699 root
.map
["objc-duplicate-class-warnings"].array
.push_back(warningNode
);
705 // add uuid of dyld cache this closure requires
706 closure
.dyldCacheUUID();
707 uuid_string_t cacheUuidStr
;
708 uuid_unparse(*closure
.dyldCacheUUID(), cacheUuidStr
);
709 root
.map
["dyld-cache-uuid"].value
= cacheUuidStr
;
711 // add top level images
712 Node
& rootImages
= root
.map
["root-images"];
713 uint32_t initImageCount
= closure
.mainExecutableImageIndex();
714 rootImages
.array
.resize(initImageCount
+1);
715 for (uint32_t i
=0; i
<= initImageCount
; ++i
) {
716 const Image image
= closure
.group().image(i
);
717 uuid_string_t uuidStr
;
718 uuid_unparse(image
->uuid(), uuidStr
);
719 rootImages
.array
[i
].value
= uuidStr
;
721 root
.map
["initial-image-count"].value
= decimal(closure
.initialImageCount());
724 root
.map
["group-num"].value
= decimal(closure
.group().groupNum());
727 __block Node cacheOverrides
;
728 closure
.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex
, uint32_t imageIndexInClosure
, uint32_t imageOffset
, bool& stop
) {
730 patch
.map
["patch-index"].value
= decimal(patchTableIndex
);
731 patch
.map
["replacement"].value
= "{closure[" + decimal(imageIndexInClosure
) + "]+" + hex(imageOffset
) + "}";
732 cacheOverrides
.array
.push_back(patch
);
734 if ( !cacheOverrides
.array
.empty() )
735 root
.map
["dyld-cache-overrides"].array
= cacheOverrides
.array
;
740 void printImageAsJSON(const Image
* image
, const Array
<const ImageArray
*>& imagesArrays
,
741 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
743 Node root
= buildImageNode(image
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
, nullptr, {});
744 printJSON(root
, 0, out
);
747 void printDyldCacheImagesAsJSON(const DyldSharedCache
* dyldCache
, bool printFixups
, bool printRaw
, std::ostream
& out
)
749 const dyld3::closure::ImageArray
* dylibs
= dyldCache
->cachedDylibsImageArray();
750 STACK_ALLOC_ARRAY(const ImageArray
*, imagesArrays
, 2);
751 imagesArrays
.push_back(dylibs
);
753 Node root
= buildImageArrayNode(dylibs
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
, nullptr, {});
754 printJSON(root
, 0, out
);
757 void printClosureAsJSON(const LaunchClosure
* cls
, const Array
<const ImageArray
*>& imagesArrays
,
758 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
760 Node root
= buildClosureNode(cls
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
);
761 printJSON(root
, 0, out
);
764 void printClosureAsJSON(const DlopenClosure
* cls
, const Array
<const ImageArray
*>& imagesArrays
,
765 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
767 Node root
= buildClosureNode(cls
, imagesArrays
, printFixups
, printRaw
, false);
768 printJSON(root
, 0, out
);
772 } // namespace closure