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 ") + hex4(target
.image
.imageNum
) + "-" + 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::mainEntry
:
144 case TypedBytes::Type::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
:
156 case TypedBytes::Type::warning
:
158 case TypedBytes::Type::duplicateClassesTable
:
159 return "duplicateClassesTable";
160 case TypedBytes::Type::progVars
:
161 return "programVars";
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 // if ( image->cwdMustBeThisDir() )
211 // imageNode.map["cwd-must-be-this-dir"].value = "true";
212 if ( !image
->inDyldCache() ) {
213 uint32_t csFileOffset
;
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
);
219 // uint32_t fpTextOffset;
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);
227 if ( image
->hasFileModTimeAndInode(inode
, mTime
) ) {
228 imageNode
.map
["file-mod-time"].value
= hex(inode
);
229 imageNode
.map
["file-inode"].value
= hex(mTime
);
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;
239 cdHashStr
+= '0' + nibbleH
;
241 cdHashStr
+= 'a' + (nibbleH
-10);
243 cdHashStr
+= '0' + nibbleL
;
245 cdHashStr
+= 'a' + (nibbleL
-10);
247 if ( cdHashStr
!= "0000000000000000000000000000000000000000" ) {
249 hashNode
.value
= cdHashStr
;
250 imageNode
.map
["cd-hashes"].array
.push_back(hashNode
);
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
) {
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
) {
267 segInfoNode
.map
["permissions"].value
= "--";
270 segInfoNode
.map
["permissions"].value
= "r";
273 segInfoNode
.map
["permissions"].value
= "ro"; // r/w then r/o
276 segInfoNode
.map
["permissions"].value
= "rw";
279 segInfoNode
.map
["permissions"].value
= "rx";
282 segInfoNode
.map
["permissions"].value
= "??";
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
);
294 image
->forEachFixup(^(uint64_t imageOffsetToRebase
, bool &stop
) {
296 imageNode
.map
["fixups"].map
[hex8(imageOffsetToRebase
)].value
= "rebase";
297 }, ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
299 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= printTarget(imagesArrays
, target
);
300 }, ^(uint64_t startsStructImageOffset
, const Array
<Image::ResolvedSymbolTarget
>& targets
, bool& stop
) {
302 imageNode
.map
["fixups-chain-starts-offset"].value
= hex8(startsStructImageOffset
);
303 for (const Image::ResolvedSymbolTarget
& target
: targets
) {
305 targetNode
.value
= printTarget(imagesArrays
, target
);
306 imageNode
.map
["fixups-targets"].array
.push_back(targetNode
);
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
) {
314 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= printTarget(imagesArrays
, target
);
315 }, ^(uint64_t imageOffsetToFixup
, uint32_t selectorIndex
, bool inSharedCache
, bool &stop
) {
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
;
325 bool gotLocation
= selOpt
->getStringLocation(selectorIndex
, selImages
, imageNum
, vmOffset
);
327 target
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
328 target
.image
.imageNum
= imageNum
;
329 target
.image
.offset
= vmOffset
;
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";
339 image
->forEachTextReloc(^(uint32_t imageOffsetToRebase
, bool &stop
) {
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
);
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
) {
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
);
365 implNode
.map
["usage-sites"].array
.push_back(siteNode
);
367 imageNode
.map
["patches"].array
.push_back(implNode
);
374 image
->forEachDependentImage(^(uint32_t depIndex
, Image::LinkKind kind
, ImageNum imageNum
, bool& stop
) {
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();
381 case Image::LinkKind::regular
:
382 depMapNode
.map
["link"].value
= "regular";
384 case Image::LinkKind::reExport
:
385 depMapNode
.map
["link"].value
= "re-export";
387 case Image::LinkKind::upward
:
388 depMapNode
.map
["link"].value
= "upward";
390 case Image::LinkKind::weak
:
391 depMapNode
.map
["link"].value
= "weak";
394 imageNode
.map
["dependents"].array
.push_back(depMapNode
);
398 bool usesInitsSection
= image
->forEachInitializerSection(^(uint32_t sectionOffset
, uint32_t sectionSize
) {
400 initSectNode
.map
["offset"].value
= hex(sectionOffset
);
401 initSectNode
.map
["size"].value
= hex(sectionSize
);
402 imageNode
.map
["initializers-section"].array
.push_back(initSectNode
);
404 if ( !usesInitsSection
) {
405 image
->forEachInitializer(nullptr, ^(const void* initializer
) {
407 initNode
.value
= hex((long)initializer
);
408 imageNode
.map
["initializer-offsets"].array
.push_back(initNode
);
411 __block Node initBeforeNode
;
412 image
->forEachImageToInitBefore(^(ImageNum imageToInit
, bool& stop
) {
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
);
420 // add static terminators
421 image
->forEachTerminator(nullptr, ^(const void* terminator
) {
423 termNode
.value
= hex8((long)terminator
);
424 imageNode
.map
["terminator-offsets"].array
.push_back(termNode
);
427 ImageNum cacheImageNum
;
428 if ( image
->isOverrideOfDyldCacheImage(cacheImageNum
) ) {
429 imageNode
.map
["override-of-dyld-cache-image"].value
= ImageArray::findImage(imagesArrays
, cacheImageNum
)->path();
432 if ( image
->inDyldCache() && image
->overridableDylib() ) {
433 imageNode
.map
["overridable-dylib"].value
= "true";
438 // add things to init before this image
439 __block Node initBeforeNode
;
440 image
->forEachInitBefore(groupList
, ^(Image beforeImage
) {
442 beforeNode
.value
= beforeimage
->path();
443 imageNode
.map
["initializer-order"].array
.push_back(beforeNode
);
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();
453 image
->forEachDOF(nullptr, ^(const void* section
) {
455 initNode
.value
= hex((long)section
);
456 imageNode
.map
["dof-offsets"].array
.push_back(initNode
);
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
)
470 imageArray
->forEachImage(^(const Image
* image
, bool& stop
) {
471 images
.array
.push_back(buildImageNode(image
, imagesArrays
, printFixups
, printDependentsDetails
, printRaw
, dyldCache
, selOpt
, selImages
));
477 static Node
buildClosureNode(const DlopenClosure
* closure
, const Array
<const ImageArray
*>& imagesArrays
,
478 bool printFixups
, bool printRaw
, bool printDependentsDetails
)
481 root
.map
["images"] = buildImageArrayNode(closure
->images(), imagesArrays
,
482 printFixups
, printDependentsDetails
, printRaw
,
483 nullptr, nullptr, {});
485 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
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
);
496 static Node
buildClosureNode(const LaunchClosure
* closure
, const Array
<const ImageArray
*>& imagesArrays
,
497 bool printFixups
, bool printDependentsDetails
, bool printRaw
,
498 const DyldSharedCache
* dyldCache
)
502 Array
<Image::ObjCSelectorImage
> selectorImages
;
503 const closure::ObjCSelectorOpt
* selectorHashTable
= nullptr;
504 bool hasPreoptimizedObjCSelectors
= closure
->selectorHashTable(selectorImages
, selectorHashTable
);
506 root
.map
["images"] = buildImageArrayNode(closure
->images(), imagesArrays
, printFixups
,
507 printDependentsDetails
, printRaw
,
508 dyldCache
, selectorHashTable
, selectorImages
);
511 __block Node attributes
;
512 closure
->forEachAttribute(^(const TypedBytes
*typedBytes
, bool &stop
) {
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
);
519 root
.map
["attributes"] = attributes
;
523 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
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
);
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
);
537 Image::ResolvedSymbolTarget libdyldEntry
;
538 closure
->libDyldEntry(libdyldEntry
);
539 root
.map
["libdyld-entry"].value
= printTarget(imagesArrays
, libdyldEntry
);
541 root
.map
["uses-@paths"].value
= (closure
->usedAtPaths() ? "true" : "false");
542 root
.map
["uses-fallback-paths"].value
= (closure
->usedFallbackPaths() ? "true" : "false");
544 // add missing files array if they exist
545 closure
->forEachMustBeMissingFile(^(const char* path
, bool& stop
) {
547 fileNode
.value
= path
;
548 root
.map
["must-be-missing-files"].array
.push_back(fileNode
);
551 // add skipped files array if they exist
552 closure
->forEachSkipIfExistsFile(^(const LaunchClosure::SkippedFile
&file
, bool &stop
) {
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
);
560 // add interposing info, if any
561 closure
->forEachInterposingTuple(^(const InterposingTuple
& tuple
, bool& stop
) {
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
);
568 root
.map
["initial-image-count"].value
= decimal(closure
->initialLoadCount());
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 ) {
575 strncpy(key
, keyEqualValue
, equ
-keyEqualValue
);
576 key
[equ
-keyEqualValue
] = '\0';
577 root
.map
["env-vars"].map
[key
].value
= equ
+1;
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
;
592 targetNode
.value
= printTarget(imagesArrays
, target
);
593 selectorsNode
.array
.push_back(targetNode
);
596 root
.map
["objc-selectors"] = selectorsNode
;
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
;
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
;
615 targetNode
.value
= printTarget(imagesArrays
, target
);
618 classNode
.map
["name"] = targetNode
;
619 classesNode
.array
.push_back(classNode
);
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
;
628 implNode
.value
= printTarget(imagesArrays
, implTarget
);
629 classesNode
.array
.back().map
["implementations"].array
.push_back(implNode
);
632 root
.map
["objc-classes"] = classesNode
;
635 if ( protocolHashTable
!= nullptr ) {
636 __block Node protocolsNode
;
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
;
647 targetNode
.value
= printTarget(imagesArrays
, target
);
650 protocolNode
.map
["name"] = targetNode
;
651 protocolsNode
.array
.push_back(protocolNode
);
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
;
660 implNode
.value
= printTarget(imagesArrays
, implTarget
);
661 protocolsNode
.array
.back().map
["implementations"].array
.push_back(implNode
);
664 root
.map
["objc-protocols"] = protocolsNode
;
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
);
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
;
683 targetNode
.value
= printTarget(imagesArrays
, target
);
685 Node duplicateClassNode
;
686 duplicateClassNode
.map
["name"].value
= className
;
687 duplicateClassNode
.map
["implementation"] = targetNode
;
688 duplicateClassNode
.array
.push_back(targetNode
);
690 duplicateClassesNode
.array
.push_back(duplicateClassNode
);
693 root
.map
["objc-duplicate-classes"] = duplicateClassesNode
;
696 // add warnings for objc if they exist
697 closure
->forEachWarning(Closure::Warning::duplicateObjCClass
, ^(const char *warning
, bool &stop
) {
699 warningNode
.value
= warning
;
700 root
.map
["objc-duplicate-class-warnings"].array
.push_back(warningNode
);
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
);
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
;
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
;
727 root
.map
["initial-image-count"].value
= decimal(closure
.initialImageCount());
730 root
.map
["group-num"].value
= decimal(closure
.group().groupNum());
733 __block Node cacheOverrides
;
734 closure
.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex
, uint32_t imageIndexInClosure
, uint32_t imageOffset
, bool& stop
) {
736 patch
.map
["patch-index"].value
= decimal(patchTableIndex
);
737 patch
.map
["replacement"].value
= "{closure[" + decimal(imageIndexInClosure
) + "]+" + hex(imageOffset
) + "}";
738 cacheOverrides
.array
.push_back(patch
);
740 if ( !cacheOverrides
.array
.empty() )
741 root
.map
["dyld-cache-overrides"].array
= cacheOverrides
.array
;
746 void printImageAsJSON(const Image
* image
, const Array
<const ImageArray
*>& imagesArrays
,
747 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
749 Node root
= buildImageNode(image
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
, nullptr, {});
750 printJSON(root
, 0, out
);
753 void printDyldCacheImagesAsJSON(const DyldSharedCache
* dyldCache
, bool printFixups
, bool printRaw
, std::ostream
& out
)
755 const dyld3::closure::ImageArray
* dylibs
= dyldCache
->cachedDylibsImageArray();
756 STACK_ALLOC_ARRAY(const ImageArray
*, imagesArrays
, 2);
757 imagesArrays
.push_back(dylibs
);
759 Node root
= buildImageArrayNode(dylibs
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
, nullptr, {});
760 printJSON(root
, 0, out
);
763 void printClosureAsJSON(const LaunchClosure
* cls
, const Array
<const ImageArray
*>& imagesArrays
,
764 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
766 Node root
= buildClosureNode(cls
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
);
767 printJSON(root
, 0, out
);
770 void printClosureAsJSON(const DlopenClosure
* cls
, const Array
<const ImageArray
*>& imagesArrays
,
771 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
773 Node root
= buildClosureNode(cls
, imagesArrays
, printFixups
, printRaw
, false);
774 printJSON(root
, 0, out
);
778 } // namespace closure