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";
162 case TypedBytes::Type::progVars
:
163 return "programVars";
167 static Node
buildImageNode(const Image
* image
, const Array
<const ImageArray
*>& imagesArrays
, bool printFixups
,
168 bool printDependentsDetails
, bool printRaw
,
169 const DyldSharedCache
* dyldCache
, const closure::ObjCSelectorOpt
* selOpt
,
170 const Array
<closure::Image::ObjCSelectorImage
>& selImages
)
172 __block Node imageNode
;
174 if ( image
->isInvalid() )
177 imageNode
.map
["image-num"].value
= hex4(image
->imageNum());
178 imageNode
.map
["path"].value
= image
->path();
181 __block Node attributes
;
182 image
->forEachAttribute(^(const TypedBytes
*typedBytes
, bool &stop
) {
184 anAttribute
.map
["type"].value
= decimal((uint32_t)typedBytes
->type
);
185 anAttribute
.map
["type-name"].value
= nameForType((TypedBytes::Type
)typedBytes
->type
);
186 anAttribute
.map
["length"].value
= decimal(typedBytes
->payloadLength
);
187 attributes
.array
.push_back(anAttribute
);
189 imageNode
.map
["attributes"] = attributes
;
193 __block Node imageAliases
;
194 image
->forEachAlias(^(const char* aliasPath
, bool& stop
) {
196 anAlias
.value
= aliasPath
;
197 imageAliases
.array
.push_back(anAlias
);
199 if ( !imageAliases
.array
.empty() )
200 imageNode
.map
["aliases"] = imageAliases
;
202 if ( image
->getUuid(uuid
) ) {
203 uuid_string_t uuidStr
;
204 uuid_unparse(uuid
, uuidStr
);
205 imageNode
.map
["uuid"].value
= uuidStr
;
207 imageNode
.map
["has-objc"].value
= (image
->hasObjC() ? "true" : "false");
208 imageNode
.map
["has-weak-defs"].value
= (image
->hasWeakDefs() ? "true" : "false");
209 imageNode
.map
["has-plus-loads"].value
= (image
->mayHavePlusLoads() ? "true" : "false");
210 imageNode
.map
["never-unload"].value
= (image
->neverUnload() ? "true" : "false");
211 imageNode
.map
["has-precomputed-objc"].value
= (image
->hasPrecomputedObjC() ? "true" : "false");
212 // imageNode.map["platform-binary"].value = (image->isPlatformBinary() ? "true" : "false");
213 // if ( image->cwdMustBeThisDir() )
214 // imageNode.map["cwd-must-be-this-dir"].value = "true";
215 if ( !image
->inDyldCache() ) {
216 uint32_t csFileOffset
;
218 if ( image
->hasCodeSignature(csFileOffset
, csSize
) ) {
219 imageNode
.map
["code-sign-location"].map
["offset"].value
= hex(csFileOffset
);
220 imageNode
.map
["code-sign-location"].map
["size"].value
= hex(csSize
);
222 // uint32_t fpTextOffset;
224 // if ( image->isFairPlayEncrypted(fpTextOffset, fpSize) ) {
225 // imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset);
226 // imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize);
230 if ( image
->hasFileModTimeAndInode(inode
, mTime
) ) {
231 imageNode
.map
["file-mod-time"].value
= hex(inode
);
232 imageNode
.map
["file-inode"].value
= hex(mTime
);
234 image
->forEachCDHash(^(const uint8_t *cdHash
, bool& stop
) {
235 std::string cdHashStr
;
236 cdHashStr
.reserve(24);
237 for (int i
=0; i
< 20; ++i
) {
238 uint8_t byte
= cdHash
[i
];
239 uint8_t nibbleL
= byte
& 0x0F;
240 uint8_t nibbleH
= byte
>> 4;
242 cdHashStr
+= '0' + nibbleH
;
244 cdHashStr
+= 'a' + (nibbleH
-10);
246 cdHashStr
+= '0' + nibbleL
;
248 cdHashStr
+= 'a' + (nibbleL
-10);
250 if ( cdHashStr
!= "0000000000000000000000000000000000000000" ) {
252 hashNode
.value
= cdHashStr
;
253 imageNode
.map
["cd-hashes"].array
.push_back(hashNode
);
256 imageNode
.map
["total-vm-size"].value
= hex(image
->vmSizeToMap());
257 uint64_t sliceOffset
= image
->sliceOffsetInFile();
258 if ( sliceOffset
!= 0 )
259 imageNode
.map
["file-offset-of-slice"].value
= hex(sliceOffset
);
260 //if ( image->hasTextRelocs() )
261 // imageNode.map["has-text-relocs"].value = "true";
262 image
->forEachDiskSegment(^(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool laterReadOnly
, bool& stop
) {
264 segInfoNode
.map
["file-offset"].value
= hex(fileOffset
);
265 segInfoNode
.map
["file-size"].value
= hex(fileSize
);
266 segInfoNode
.map
["vm-size"].value
= hex(vmSize
);
267 imageNode
.map
["mappings"].array
.push_back(segInfoNode
);
268 switch ( permissions
) {
270 segInfoNode
.map
["permissions"].value
= "--";
273 segInfoNode
.map
["permissions"].value
= "r";
276 segInfoNode
.map
["permissions"].value
= "ro"; // r/w then r/o
279 segInfoNode
.map
["permissions"].value
= "rw";
282 segInfoNode
.map
["permissions"].value
= "rx";
285 segInfoNode
.map
["permissions"].value
= "??";
291 image
->forEachFixup(^(uint64_t imageOffsetToRebase
, bool &stop
) {
293 imageNode
.map
["fixups"].map
[hex8(imageOffsetToRebase
)].value
= "rebase";
294 }, ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
296 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= printTarget(imagesArrays
, target
);
297 }, ^(uint64_t startsStructImageOffset
, const Array
<Image::ResolvedSymbolTarget
>& targets
, bool& stop
) {
299 imageNode
.map
["fixups-chain-starts-offset"].value
= hex8(startsStructImageOffset
);
300 for (const Image::ResolvedSymbolTarget
& target
: targets
) {
302 targetNode
.value
= printTarget(imagesArrays
, target
);
303 imageNode
.map
["fixups-targets"].array
.push_back(targetNode
);
306 }, ^(uint64_t imageOffsetToFixup
) {
307 // fixupObjCImageInfo
308 imageNode
.map
["fixups"].map
[hex8(imageOffsetToFixup
)].value
= "objc image info pre-optimized by dyld flag";
309 }, ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
311 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= printTarget(imagesArrays
, target
);
312 }, ^(uint64_t imageOffsetToFixup
, uint32_t selectorIndex
, bool inSharedCache
, bool &stop
) {
314 Image::ResolvedSymbolTarget target
;
315 if ( inSharedCache
) {
316 const char* selectorString
= dyldCache
->objcOpt()->selopt()->getEntryForIndex(selectorIndex
);
317 target
.sharedCache
.kind
= Image::ResolvedSymbolTarget::kindSharedCache
;
318 target
.sharedCache
.offset
= (uint64_t)selectorString
- (uint64_t)dyldCache
;
322 bool gotLocation
= selOpt
->getStringLocation(selectorIndex
, selImages
, imageNum
, vmOffset
);
324 target
.image
.kind
= Image::ResolvedSymbolTarget::kindImage
;
325 target
.image
.imageNum
= imageNum
;
326 target
.image
.offset
= vmOffset
;
328 imageNode
.map
["fixups"].map
[hex8(imageOffsetToFixup
)].value
= printTarget(imagesArrays
, target
);
329 }, ^(uint64_t imageOffsetToFixup
, bool &stop
) {
330 // fixupObjCStableSwift
331 imageNode
.map
["fixups"].map
[hex8(imageOffsetToFixup
)].value
= "objc set stable Swift";
332 }, ^(uint64_t imageOffsetToFixup
, bool &stop
) {
333 // fixupObjCMethodList
334 imageNode
.map
["fixups"].map
[hex8(imageOffsetToFixup
)].value
= "objc set fixed up method list";
336 image
->forEachTextReloc(^(uint32_t imageOffsetToRebase
, bool &stop
) {
338 imageNode
.map
["fixups"].map
[hex8(imageOffsetToRebase
)].value
= "text rebase";
339 }, ^(uint32_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
340 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= "text " + printTarget(imagesArrays
, target
);
346 if ( dyldCache
!= nullptr ) {
347 uint32_t imageIndex
= image
->imageNum() - (uint32_t)dyldCache
->cachedDylibsImageArray()->startImageNum();
348 dyldCache
->forEachPatchableExport(imageIndex
, ^(uint32_t cacheOffsetOfImpl
, const char* name
) {
349 __block Node implNode
;
350 implNode
.map
["name"].value
= name
;
351 implNode
.map
["impl-cache-offset"].value
= hex8(cacheOffsetOfImpl
);
352 dyldCache
->forEachPatchableUseOfExport(imageIndex
, cacheOffsetOfImpl
, ^(dyld_cache_patchable_location patchLocation
) {
354 siteNode
.map
["cache-offset"].value
= hex8(patchLocation
.cacheOffset
);
355 if ( patchLocation
.addend
!= 0 )
356 siteNode
.map
["addend"].value
= hex(patchLocation
.addend
);
357 if ( patchLocation
.authenticated
!= 0 ) {
358 siteNode
.map
["key"].value
= DyldSharedCache::keyName(patchLocation
);
359 siteNode
.map
["address-diversity"].value
= patchLocation
.usesAddressDiversity
? "true" : "false";
360 siteNode
.map
["discriminator"].value
= hex4(patchLocation
.discriminator
);
362 implNode
.map
["usage-sites"].array
.push_back(siteNode
);
364 imageNode
.map
["patches"].array
.push_back(implNode
);
371 image
->forEachDependentImage(^(uint32_t depIndex
, Image::LinkKind kind
, ImageNum imageNum
, bool& stop
) {
373 const Image
* depImage
= ImageArray::findImage(imagesArrays
, imageNum
);
374 depMapNode
.map
["image-num"].value
= hex4(imageNum
);
375 if ( depImage
!= nullptr )
376 depMapNode
.map
["path"].value
= depImage
->path();
378 case Image::LinkKind::regular
:
379 depMapNode
.map
["link"].value
= "regular";
381 case Image::LinkKind::reExport
:
382 depMapNode
.map
["link"].value
= "re-export";
384 case Image::LinkKind::upward
:
385 depMapNode
.map
["link"].value
= "upward";
387 case Image::LinkKind::weak
:
388 depMapNode
.map
["link"].value
= "weak";
391 imageNode
.map
["dependents"].array
.push_back(depMapNode
);
395 bool usesInitsSection
= image
->forEachInitializerSection(^(uint32_t sectionOffset
, uint32_t sectionSize
) {
397 initSectNode
.map
["offset"].value
= hex(sectionOffset
);
398 initSectNode
.map
["size"].value
= hex(sectionSize
);
399 imageNode
.map
["initializers-section"].array
.push_back(initSectNode
);
401 if ( !usesInitsSection
) {
402 image
->forEachInitializer(nullptr, ^(const void* initializer
) {
404 initNode
.value
= hex((long)initializer
);
405 imageNode
.map
["initializer-offsets"].array
.push_back(initNode
);
408 __block Node initBeforeNode
;
409 image
->forEachImageToInitBefore(^(ImageNum imageToInit
, bool& stop
) {
411 const Image
* initImage
= ImageArray::findImage(imagesArrays
, imageToInit
);
412 assert(initImage
!= nullptr);
413 beforeNode
.value
= initImage
->path();
414 imageNode
.map
["initializer-order"].array
.push_back(beforeNode
);
417 // add static terminators
418 image
->forEachTerminator(nullptr, ^(const void* terminator
) {
420 termNode
.value
= hex8((long)terminator
);
421 imageNode
.map
["terminator-offsets"].array
.push_back(termNode
);
424 ImageNum cacheImageNum
;
425 if ( image
->isOverrideOfDyldCacheImage(cacheImageNum
) ) {
426 imageNode
.map
["override-of-dyld-cache-image"].value
= ImageArray::findImage(imagesArrays
, cacheImageNum
)->path();
431 // add things to init before this image
432 __block Node initBeforeNode
;
433 image
->forEachInitBefore(groupList
, ^(Image beforeImage
) {
435 beforeNode
.value
= beforeimage
->path();
436 imageNode
.map
["initializer-order"].array
.push_back(beforeNode
);
439 // add override info if relevant
440 group
.forEachImageRefOverride(groupList
, ^(Image standardDylib
, Image overrideDylib
, bool& stop
) {
441 if ( overrideDylib
.binaryData() == image
->binaryData() ) {
442 imageNode
.map
["override-of-cached-dylib"].value
= standardDylib
.path();
446 image
->forEachDOF(nullptr, ^(const void* section
) {
448 initNode
.value
= hex((long)section
);
449 imageNode
.map
["dof-offsets"].array
.push_back(initNode
);
457 static Node
buildImageArrayNode(const ImageArray
* imageArray
, const Array
<const ImageArray
*>& imagesArrays
,
458 bool printFixups
, bool printDependentsDetails
, bool printRaw
,
459 const DyldSharedCache
* dyldCache
, const closure::ObjCSelectorOpt
* selOpt
,
460 const Array
<closure::Image::ObjCSelectorImage
>& selImages
)
463 imageArray
->forEachImage(^(const Image
* image
, bool& stop
) {
464 images
.array
.push_back(buildImageNode(image
, imagesArrays
, printFixups
, printDependentsDetails
, printRaw
, dyldCache
, selOpt
, selImages
));
470 static Node
buildClosureNode(const DlopenClosure
* closure
, const Array
<const ImageArray
*>& imagesArrays
,
471 bool printFixups
, bool printRaw
, bool printDependentsDetails
)
474 root
.map
["images"] = buildImageArrayNode(closure
->images(), imagesArrays
,
475 printFixups
, printDependentsDetails
, printRaw
,
476 nullptr, nullptr, {});
478 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
480 patchNode
.map
["func-dyld-cache-offset"].value
= hex8(patchEntry
.exportCacheOffset
);
481 patchNode
.map
["func-image-num"].value
= hex8(patchEntry
.overriddenDylibInCache
);
482 patchNode
.map
["replacement"].value
= printTarget(imagesArrays
, patchEntry
.replacement
);
483 root
.map
["dyld-cache-fixups"].array
.push_back(patchNode
);
489 static Node
buildClosureNode(const LaunchClosure
* closure
, const Array
<const ImageArray
*>& imagesArrays
,
490 bool printFixups
, bool printDependentsDetails
, bool printRaw
,
491 const DyldSharedCache
* dyldCache
)
495 Array
<Image::ObjCSelectorImage
> selectorImages
;
496 const closure::ObjCSelectorOpt
* selectorHashTable
= nullptr;
497 bool hasPreoptimizedObjCSelectors
= closure
->selectorHashTable(selectorImages
, selectorHashTable
);
499 root
.map
["images"] = buildImageArrayNode(closure
->images(), imagesArrays
, printFixups
,
500 printDependentsDetails
, printRaw
,
501 dyldCache
, selectorHashTable
, selectorImages
);
504 __block Node attributes
;
505 closure
->forEachAttribute(^(const TypedBytes
*typedBytes
, bool &stop
) {
507 anAttribute
.map
["type"].value
= decimal((uint32_t)typedBytes
->type
);
508 anAttribute
.map
["type-name"].value
= nameForType((TypedBytes::Type
)typedBytes
->type
);
509 anAttribute
.map
["length"].value
= decimal(typedBytes
->payloadLength
);
510 attributes
.array
.push_back(anAttribute
);
512 root
.map
["attributes"] = attributes
;
516 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
518 patchNode
.map
["func-dyld-cache-offset"].value
= hex8(patchEntry
.exportCacheOffset
);
519 patchNode
.map
["func-image-num"].value
= hex8(patchEntry
.overriddenDylibInCache
);
520 patchNode
.map
["replacement"].value
= printTarget(imagesArrays
, patchEntry
.replacement
);
521 root
.map
["dyld-cache-fixups"].array
.push_back(patchNode
);
524 Image::ResolvedSymbolTarget entry
;
525 if ( closure
->mainEntry(entry
) )
526 root
.map
["main"].value
= printTarget(imagesArrays
, entry
);
527 else if ( closure
->startEntry(entry
) )
528 root
.map
["start"].value
= printTarget(imagesArrays
, entry
);
530 Image::ResolvedSymbolTarget libdyldEntry
;
531 closure
->libDyldEntry(libdyldEntry
);
532 root
.map
["libdyld-entry"].value
= printTarget(imagesArrays
, libdyldEntry
);
534 root
.map
["uses-@paths"].value
= (closure
->usedAtPaths() ? "true" : "false");
535 root
.map
["uses-fallback-paths"].value
= (closure
->usedFallbackPaths() ? "true" : "false");
537 // add missing files array if they exist
538 closure
->forEachMustBeMissingFile(^(const char* path
, bool& stop
) {
540 fileNode
.value
= path
;
541 root
.map
["must-be-missing-files"].array
.push_back(fileNode
);
544 // add skipped files array if they exist
545 closure
->forEachSkipIfExistsFile(^(const LaunchClosure::SkippedFile
&file
, bool &stop
) {
547 fileNode
.map
["path"].value
= file
.path
;
548 fileNode
.map
["file-mod-time"].value
= hex(file
.mtime
);
549 fileNode
.map
["file-inode"].value
= hex(file
.inode
);
550 root
.map
["skipped-existing-files"].array
.push_back(fileNode
);
553 // add interposing info, if any
554 closure
->forEachInterposingTuple(^(const InterposingTuple
& tuple
, bool& stop
) {
556 tupleNode
.map
["stock"].value
= printTarget(imagesArrays
, tuple
.stockImplementation
);
557 tupleNode
.map
["replace"].value
= printTarget(imagesArrays
, tuple
.newImplementation
);
558 root
.map
["interposing-tuples"].array
.push_back(tupleNode
);
561 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
563 patchNode
.map
["func-dyld-cache-offset"].value
= hex8(patchEntry
.exportCacheOffset
);
564 patchNode
.map
["func-image-num"].value
= hex8(patchEntry
.overriddenDylibInCache
);
565 patchNode
.map
["replacement"].value
= printTarget(imagesArrays
, patchEntry
.replacement
);
566 root
.map
["dyld-cache-fixups"].array
.push_back(patchNode
);
569 root
.map
["initial-image-count"].value
= decimal(closure
->initialLoadCount());
571 // add env-vars if they exist
572 closure
->forEachEnvVar(^(const char* keyEqualValue
, bool& stop
) {
573 const char* equ
= strchr(keyEqualValue
, '=');
574 if ( equ
!= nullptr ) {
576 strncpy(key
, keyEqualValue
, equ
-keyEqualValue
);
577 key
[equ
-keyEqualValue
] = '\0';
578 root
.map
["env-vars"].map
[key
].value
= equ
+1;
582 if (hasPreoptimizedObjCSelectors
) {
583 __block Node selectorsNode
;
584 selectorHashTable
->forEachString(selectorImages
,
585 ^(uint64_t selVMOffset
, ImageNum imageNum
) {
586 // Convert to a target we can get a real name for
587 dyld3::closure::Image::ResolvedSymbolTarget target
;
588 target
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
589 target
.image
.imageNum
= imageNum
;
590 target
.image
.offset
= selVMOffset
;
593 targetNode
.value
= printTarget(imagesArrays
, target
);
594 selectorsNode
.array
.push_back(targetNode
);
597 root
.map
["objc-selectors"] = selectorsNode
;
600 Array
<Image::ObjCClassImage
> classImages
;
601 const ObjCClassOpt
* classHashTable
= nullptr;
602 const ObjCClassOpt
* protocolHashTable
= nullptr;
603 if (closure
->classAndProtocolHashTables(classImages
, classHashTable
, protocolHashTable
)) {
604 if ( classHashTable
!= nullptr ) {
605 __block Node classesNode
;
607 classHashTable
->forEachClass(classImages
,
608 ^(uint64_t classNameVMOffset
, ImageNum imageNum
) {
609 // Convert to a target we can get a real name for
610 dyld3::closure::Image::ResolvedSymbolTarget target
;
611 target
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
612 target
.image
.imageNum
= imageNum
;
613 target
.image
.offset
= classNameVMOffset
;
616 targetNode
.value
= printTarget(imagesArrays
, target
);
619 classNode
.map
["name"] = targetNode
;
620 classesNode
.array
.push_back(classNode
);
622 ^(uint64_t classVMOffset
, ImageNum imageNum
) {
623 dyld3::closure::Image::ResolvedSymbolTarget implTarget
;
624 implTarget
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
625 implTarget
.image
.imageNum
= imageNum
;
626 implTarget
.image
.offset
= classVMOffset
;
629 implNode
.value
= printTarget(imagesArrays
, implTarget
);
630 classesNode
.array
.back().map
["implementations"].array
.push_back(implNode
);
633 root
.map
["objc-classes"] = classesNode
;
636 if ( protocolHashTable
!= nullptr ) {
637 __block Node protocolsNode
;
639 protocolHashTable
->forEachClass(classImages
,
640 ^(uint64_t protocolNameVMOffset
, ImageNum imageNum
) {
641 // Convert to a target we can get a real name for
642 dyld3::closure::Image::ResolvedSymbolTarget target
;
643 target
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
644 target
.image
.imageNum
= imageNum
;
645 target
.image
.offset
= protocolNameVMOffset
;
648 targetNode
.value
= printTarget(imagesArrays
, target
);
651 protocolNode
.map
["name"] = targetNode
;
652 protocolsNode
.array
.push_back(protocolNode
);
654 ^(uint64_t protocolVMOffset
, ImageNum imageNum
) {
655 dyld3::closure::Image::ResolvedSymbolTarget implTarget
;
656 implTarget
.image
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindImage
;
657 implTarget
.image
.imageNum
= imageNum
;
658 implTarget
.image
.offset
= protocolVMOffset
;
661 implNode
.value
= printTarget(imagesArrays
, implTarget
);
662 protocolsNode
.array
.back().map
["implementations"].array
.push_back(implNode
);
665 root
.map
["objc-protocols"] = protocolsNode
;
669 const ObjCClassDuplicatesOpt
* duplicateClassesHashTable
= nullptr;
670 closure
->duplicateClassesHashTable(duplicateClassesHashTable
);
671 if ( duplicateClassesHashTable
!= nullptr ) {
672 __block Node duplicateClassesNode
;
673 duplicateClassesHashTable
->forEachClass(^(Image::ObjCDuplicateClass duplicateClass
) {
674 objc_opt::objc_clsopt_t
* clsOpt
= dyldCache
->objcOpt()->clsopt();
675 const char* className
= clsOpt
->getClassNameForIndex(duplicateClass
.sharedCacheClassOptIndex
);
676 const void* classImpl
= clsOpt
->getClassForIndex(duplicateClass
.sharedCacheClassOptIndex
, duplicateClass
.sharedCacheClassDuplicateIndex
);
678 // Convert to a target we can get a real name for
679 dyld3::closure::Image::ResolvedSymbolTarget target
;
680 target
.sharedCache
.kind
= dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache
;
681 target
.sharedCache
.offset
= (uint64_t)classImpl
- (uint64_t)dyldCache
;
684 targetNode
.value
= printTarget(imagesArrays
, target
);
686 Node duplicateClassNode
;
687 duplicateClassNode
.map
["name"].value
= className
;
688 duplicateClassNode
.map
["implementation"] = targetNode
;
689 duplicateClassNode
.array
.push_back(targetNode
);
691 duplicateClassesNode
.array
.push_back(duplicateClassNode
);
694 root
.map
["objc-duplicate-classes"] = duplicateClassesNode
;
697 // add warnings for objc if they exist
698 closure
->forEachWarning(Closure::Warning::duplicateObjCClass
, ^(const char *warning
, bool &stop
) {
700 warningNode
.value
= warning
;
701 root
.map
["objc-duplicate-class-warnings"].array
.push_back(warningNode
);
704 // add program vars info for old macOS binaries
705 uint32_t progVarsOffset
;
706 if ( closure
->hasProgramVars(progVarsOffset
) )
707 root
.map
["program-vars-offset"].value
= hex8(progVarsOffset
);
712 // add uuid of dyld cache this closure requires
713 closure
.dyldCacheUUID();
714 uuid_string_t cacheUuidStr
;
715 uuid_unparse(*closure
.dyldCacheUUID(), cacheUuidStr
);
716 root
.map
["dyld-cache-uuid"].value
= cacheUuidStr
;
718 // add top level images
719 Node
& rootImages
= root
.map
["root-images"];
720 uint32_t initImageCount
= closure
.mainExecutableImageIndex();
721 rootImages
.array
.resize(initImageCount
+1);
722 for (uint32_t i
=0; i
<= initImageCount
; ++i
) {
723 const Image image
= closure
.group().image(i
);
724 uuid_string_t uuidStr
;
725 uuid_unparse(image
->uuid(), uuidStr
);
726 rootImages
.array
[i
].value
= uuidStr
;
728 root
.map
["initial-image-count"].value
= decimal(closure
.initialImageCount());
731 root
.map
["group-num"].value
= decimal(closure
.group().groupNum());
734 __block Node cacheOverrides
;
735 closure
.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex
, uint32_t imageIndexInClosure
, uint32_t imageOffset
, bool& stop
) {
737 patch
.map
["patch-index"].value
= decimal(patchTableIndex
);
738 patch
.map
["replacement"].value
= "{closure[" + decimal(imageIndexInClosure
) + "]+" + hex(imageOffset
) + "}";
739 cacheOverrides
.array
.push_back(patch
);
741 if ( !cacheOverrides
.array
.empty() )
742 root
.map
["dyld-cache-overrides"].array
= cacheOverrides
.array
;
747 void printImageAsJSON(const Image
* image
, const Array
<const ImageArray
*>& imagesArrays
,
748 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
750 Node root
= buildImageNode(image
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
, nullptr, {});
751 printJSON(root
, 0, out
);
754 void printDyldCacheImagesAsJSON(const DyldSharedCache
* dyldCache
, bool printFixups
, bool printRaw
, std::ostream
& out
)
756 const dyld3::closure::ImageArray
* dylibs
= dyldCache
->cachedDylibsImageArray();
757 STACK_ALLOC_ARRAY(const ImageArray
*, imagesArrays
, 2);
758 imagesArrays
.push_back(dylibs
);
760 Node root
= buildImageArrayNode(dylibs
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
, nullptr, {});
761 printJSON(root
, 0, out
);
764 void printClosureAsJSON(const LaunchClosure
* cls
, const Array
<const ImageArray
*>& imagesArrays
,
765 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
767 Node root
= buildClosureNode(cls
, imagesArrays
, printFixups
, false, printRaw
, dyldCache
);
768 printJSON(root
, 0, out
);
771 void printClosureAsJSON(const DlopenClosure
* cls
, const Array
<const ImageArray
*>& imagesArrays
,
772 bool printFixups
, bool printRaw
, const DyldSharedCache
* dyldCache
, std::ostream
& out
)
774 Node root
= buildClosureNode(cls
, imagesArrays
, printFixups
, printRaw
, false);
775 printJSON(root
, 0, out
);
779 } // namespace closure