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 using namespace dyld3::json
;
38 static std::string
printTarget(const Array
<const ImageArray
*>& imagesArrays
, Image::ResolvedSymbolTarget target
)
40 const Image
* targetImage
;
42 switch ( target
.image
.kind
) {
43 case Image::ResolvedSymbolTarget::kindImage
:
44 targetImage
= ImageArray::findImage(imagesArrays
, target
.image
.imageNum
);
45 if ( target
.image
.offset
& 0x8000000000ULL
) {
46 uint64_t signExtend
= target
.image
.offset
| 0xFFFFFF0000000000ULL
;
47 return std::string("bind to ") + targetImage
->leafName() + " - " + hex8(-signExtend
);
50 return std::string("bind to ") + targetImage
->leafName() + " + " + hex8(target
.image
.offset
);
52 case Image::ResolvedSymbolTarget::kindSharedCache
:
53 return std::string("bind to dyld cache + ") + hex8(target
.sharedCache
.offset
);
55 case Image::ResolvedSymbolTarget::kindAbsolute
:
56 value
= target
.absolute
.value
;
57 if ( value
& 0x2000000000000000LL
)
58 value
|= 0xC000000000000000LL
;
59 return std::string("bind to absolute ") + hex(value
);
66 static Node
buildImageNode(const Image
* image
, const Array
<const ImageArray
*>& imagesArrays
, bool printFixups
, bool printDependentsDetails
, const uint8_t* cacheStart
=nullptr)
68 __block Node imageNode
;
70 if ( image
->isInvalid() )
73 imageNode
.map
["image-num"].value
= hex4(image
->imageNum());
74 imageNode
.map
["path"].value
= image
->path();
75 __block Node imageAliases
;
76 image
->forEachAlias(^(const char* aliasPath
, bool& stop
) {
78 anAlias
.value
= aliasPath
;
79 imageAliases
.array
.push_back(anAlias
);
81 if ( !imageAliases
.array
.empty() )
82 imageNode
.map
["aliases"] = imageAliases
;
84 if ( image
->getUuid(uuid
) ) {
85 uuid_string_t uuidStr
;
86 uuid_unparse(uuid
, uuidStr
);
87 imageNode
.map
["uuid"].value
= uuidStr
;
89 imageNode
.map
["has-objc"].value
= (image
->hasObjC() ? "true" : "false");
90 imageNode
.map
["has-weak-defs"].value
= (image
->hasWeakDefs() ? "true" : "false");
91 imageNode
.map
["has-plus-loads"].value
= (image
->mayHavePlusLoads() ? "true" : "false");
92 imageNode
.map
["never-unload"].value
= (image
->neverUnload() ? "true" : "false");
93 // imageNode.map["platform-binary"].value = (image->isPlatformBinary() ? "true" : "false");
94 // if ( image->cwdMustBeThisDir() )
95 // imageNode.map["cwd-must-be-this-dir"].value = "true";
96 if ( !image
->inDyldCache() ) {
97 uint32_t csFileOffset
;
99 if ( image
->hasCodeSignature(csFileOffset
, csSize
) ) {
100 imageNode
.map
["code-sign-location"].map
["offset"].value
= hex(csFileOffset
);
101 imageNode
.map
["code-sign-location"].map
["size"].value
= hex(csSize
);
103 // uint32_t fpTextOffset;
105 // if ( image->isFairPlayEncrypted(fpTextOffset, fpSize) ) {
106 // imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset);
107 // imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize);
111 if ( image
->hasFileModTimeAndInode(inode
, mTime
) ) {
112 imageNode
.map
["file-mod-time"].value
= hex(inode
);
113 imageNode
.map
["file-inode"].value
= hex(mTime
);
116 if ( image
->hasCdHash(cdHash
) ) {
117 std::string cdHashStr
;
118 cdHashStr
.reserve(24);
119 for (int i
=0; i
< 20; ++i
) {
120 uint8_t byte
= cdHash
[i
];
121 uint8_t nibbleL
= byte
& 0x0F;
122 uint8_t nibbleH
= byte
>> 4;
124 cdHashStr
+= '0' + nibbleH
;
126 cdHashStr
+= 'a' + (nibbleH
-10);
128 cdHashStr
+= '0' + nibbleL
;
130 cdHashStr
+= 'a' + (nibbleL
-10);
132 if ( cdHashStr
!= "0000000000000000000000000000000000000000" )
133 imageNode
.map
["cd-hash"].value
= cdHashStr
;
137 const uint8_t* cdHash
= image
->cdHash16();
138 std::string cdHashStr
;
139 cdHashStr
.reserve(32);
140 for (int j
=0; j
< 16; ++j
) {
141 uint8_t byte
= cdHash
[j
];
142 uint8_t nibbleL
= byte
& 0x0F;
143 uint8_t nibbleH
= byte
>> 4;
145 cdHashStr
+= '0' + nibbleH
;
147 cdHashStr
+= 'a' + (nibbleH
-10);
149 cdHashStr
+= '0' + nibbleL
;
151 cdHashStr
+= 'a' + (nibbleL
-10);
153 imageNode
.map
["file-cd-hash-16"].value
= cdHashStr
;
156 imageNode
.map
["total-vm-size"].value
= hex(image
->vmSizeToMap());
157 uint64_t sliceOffset
= image
->sliceOffsetInFile();
158 if ( sliceOffset
!= 0 )
159 imageNode
.map
["file-offset-of-slice"].value
= hex(sliceOffset
);
160 //if ( image->hasTextRelocs() )
161 // imageNode.map["has-text-relocs"].value = "true";
162 image
->forEachDiskSegment(^(uint32_t segIndex
, uint32_t fileOffset
, uint32_t fileSize
, int64_t vmOffset
, uint64_t vmSize
, uint8_t permissions
, bool& stop
) {
164 segInfoNode
.map
["file-offset"].value
= hex(fileOffset
);
165 segInfoNode
.map
["file-size"].value
= hex(fileSize
);
166 segInfoNode
.map
["vm-size"].value
= hex(vmSize
);
167 segInfoNode
.map
["permissions"].value
= hex(permissions
);
168 imageNode
.map
["mappings"].array
.push_back(segInfoNode
);
174 image
->forEachFixup(^(uint64_t imageOffsetToRebase
, bool &stop
) {
176 imageNode
.map
["fixups"].map
[hex8(imageOffsetToRebase
)].value
= "rebase";
177 }, ^(uint64_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
179 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= printTarget(imagesArrays
, target
);
180 }, ^(uint64_t imageOffsetStart
, const Array
<Image::ResolvedSymbolTarget
>& targets
, bool& stop
) {
182 imageNode
.map
["fixups"].map
[hex8(imageOffsetStart
)].value
= "chain-start";
183 for (const Image::ResolvedSymbolTarget
& target
: targets
) {
185 targetNode
.value
= printTarget(imagesArrays
, target
);
186 imageNode
.map
["fixups-targets"].array
.push_back(targetNode
);
189 image
->forEachTextReloc(^(uint32_t imageOffsetToRebase
, bool &stop
) {
191 imageNode
.map
["fixups"].map
[hex8(imageOffsetToRebase
)].value
= "text rebase";
192 }, ^(uint32_t imageOffsetToBind
, Image::ResolvedSymbolTarget target
, bool &stop
) {
193 imageNode
.map
["fixups"].map
[hex8(imageOffsetToBind
)].value
= "text " + printTarget(imagesArrays
, target
);
199 image
->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl
, const char* name
) {
200 __block Node implNode
;
201 implNode
.map
["name"].value
= name
;
202 implNode
.map
["impl-cache-offset"].value
= hex8(cacheOffsetOfImpl
);
203 image
->forEachPatchableUseOfExport(cacheOffsetOfImpl
, ^(Image::PatchableExport::PatchLocation patchLocation
) {
205 siteNode
.map
["cache-offset"].value
= hex8(patchLocation
.cacheOffset
);
206 if ( patchLocation
.addend
!= 0 )
207 siteNode
.map
["addend"].value
= hex(patchLocation
.addend
);
208 if ( patchLocation
.authenticated
!= 0 ) {
209 siteNode
.map
["key"].value
= patchLocation
.keyName();
210 siteNode
.map
["address-diversity"].value
= patchLocation
.usesAddressDiversity
? "true" : "false";
211 siteNode
.map
["discriminator"].value
= hex4(patchLocation
.discriminator
);
213 implNode
.map
["usage-sites"].array
.push_back(siteNode
);
215 imageNode
.map
["patches"].array
.push_back(implNode
);
221 image
->forEachDependentImage(^(uint32_t depIndex
, Image::LinkKind kind
, ImageNum imageNum
, bool& stop
) {
223 const Image
* depImage
= ImageArray::findImage(imagesArrays
, imageNum
);
224 depMapNode
.map
["image-num"].value
= hex4(imageNum
);
225 if ( depImage
!= nullptr )
226 depMapNode
.map
["path"].value
= depImage
->path();
228 case Image::LinkKind::regular
:
229 depMapNode
.map
["link"].value
= "regular";
231 case Image::LinkKind::reExport
:
232 depMapNode
.map
["link"].value
= "re-export";
234 case Image::LinkKind::upward
:
235 depMapNode
.map
["link"].value
= "upward";
237 case Image::LinkKind::weak
:
238 depMapNode
.map
["link"].value
= "weak";
241 imageNode
.map
["dependents"].array
.push_back(depMapNode
);
245 image
->forEachInitializer(nullptr, ^(const void* initializer
) {
247 initNode
.value
= hex((long)initializer
);
248 imageNode
.map
["initializer-offsets"].array
.push_back(initNode
);
251 __block Node initBeforeNode
;
252 image
->forEachImageToInitBefore(^(ImageNum imageToInit
, bool& stop
) {
254 const Image
* initImage
= ImageArray::findImage(imagesArrays
, imageToInit
);
255 assert(initImage
!= nullptr);
256 beforeNode
.value
= initImage
->path();
257 imageNode
.map
["initializer-order"].array
.push_back(beforeNode
);
260 ImageNum cacheImageNum
;
261 if ( image
->isOverrideOfDyldCacheImage(cacheImageNum
) ) {
262 imageNode
.map
["override-of-dyld-cache-image"].value
= ImageArray::findImage(imagesArrays
, cacheImageNum
)->path();
267 // add things to init before this image
268 __block Node initBeforeNode
;
269 image
->forEachInitBefore(groupList
, ^(Image beforeImage
) {
271 beforeNode
.value
= beforeimage
->path();
272 imageNode
.map
["initializer-order"].array
.push_back(beforeNode
);
275 // add override info if relevant
276 group
.forEachImageRefOverride(groupList
, ^(Image standardDylib
, Image overrideDylib
, bool& stop
) {
277 if ( overrideDylib
.binaryData() == image
->binaryData() ) {
278 imageNode
.map
["override-of-cached-dylib"].value
= standardDylib
.path();
282 image
->forEachDOF(nullptr, ^(const void* section
) {
284 initNode
.value
= hex((long)section
);
285 imageNode
.map
["dof-offsets"].array
.push_back(initNode
);
293 static Node
buildImageArrayNode(const ImageArray
* imageArray
, const Array
<const ImageArray
*>& imagesArrays
, bool printFixups
, bool printDependentsDetails
, const uint8_t* cacheStart
=nullptr)
296 imageArray
->forEachImage(^(const Image
* image
, bool& stop
) {
297 images
.array
.push_back(buildImageNode(image
, imagesArrays
, printFixups
, printDependentsDetails
, cacheStart
));
303 static Node
buildClosureNode(const DlopenClosure
* closure
, const Array
<const ImageArray
*>& imagesArrays
, bool printFixups
, bool printDependentsDetails
)
306 root
.map
["images"] = buildImageArrayNode(closure
->images(), imagesArrays
, printFixups
, printDependentsDetails
);
308 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
310 patchNode
.map
["func-dyld-cache-offset"].value
= hex8(patchEntry
.exportCacheOffset
);
311 patchNode
.map
["func-image-num"].value
= hex8(patchEntry
.overriddenDylibInCache
);
312 patchNode
.map
["replacement"].value
= printTarget(imagesArrays
, patchEntry
.replacement
);
313 root
.map
["dyld-cache-fixups"].array
.push_back(patchNode
);
319 static Node
buildClosureNode(const LaunchClosure
* closure
, const Array
<const ImageArray
*>& imagesArrays
, bool printFixups
, bool printDependentsDetails
)
322 root
.map
["images"] = buildImageArrayNode(closure
->images(), imagesArrays
, printFixups
, printDependentsDetails
);
324 Image::ResolvedSymbolTarget entry
;
325 if ( closure
->mainEntry(entry
) )
326 root
.map
["main"].value
= printTarget(imagesArrays
, entry
);
327 else if ( closure
->startEntry(entry
) )
328 root
.map
["start"].value
= printTarget(imagesArrays
, entry
);
330 Image::ResolvedSymbolTarget libdyldEntry
;
331 closure
->libDyldEntry(libdyldEntry
);
332 root
.map
["libdyld-entry"].value
= printTarget(imagesArrays
, libdyldEntry
);
334 root
.map
["uses-@paths"].value
= (closure
->usedAtPaths() ? "true" : "false");
335 root
.map
["uses-fallback-paths"].value
= (closure
->usedFallbackPaths() ? "true" : "false");
337 // add missing files array if they exist
338 closure
->forEachMustBeMissingFile(^(const char* path
, bool& stop
) {
340 fileNode
.value
= path
;
341 root
.map
["must-be-missing-files"].array
.push_back(fileNode
);
344 // add interposing info, if any
345 closure
->forEachInterposingTuple(^(const InterposingTuple
& tuple
, bool& stop
) {
347 tupleNode
.map
["stock"].value
= printTarget(imagesArrays
, tuple
.stockImplementation
);
348 tupleNode
.map
["replace"].value
= printTarget(imagesArrays
, tuple
.newImplementation
);
349 root
.map
["interposing-tuples"].array
.push_back(tupleNode
);
352 closure
->forEachPatchEntry(^(const Closure::PatchEntry
& patchEntry
) {
354 patchNode
.map
["func-dyld-cache-offset"].value
= hex8(patchEntry
.exportCacheOffset
);
355 patchNode
.map
["func-image-num"].value
= hex8(patchEntry
.overriddenDylibInCache
);
356 patchNode
.map
["replacement"].value
= printTarget(imagesArrays
, patchEntry
.replacement
);
357 root
.map
["dyld-cache-fixups"].array
.push_back(patchNode
);
360 root
.map
["initial-image-count"].value
= decimal(closure
->initialLoadCount());
365 // add env-vars if they exist
366 closure
->forEachEnvVar(^(const char* keyEqualValue
, bool& stop
) {
367 const char* equ
= strchr(keyEqualValue
, '=');
368 if ( equ
!= nullptr ) {
370 strncpy(key
, keyEqualValue
, equ
-keyEqualValue
);
371 key
[equ
-keyEqualValue
] = '\0';
372 root
.map
["env-vars"].map
[key
].value
= equ
+1;
377 // add uuid of dyld cache this closure requires
378 closure
.dyldCacheUUID();
379 uuid_string_t cacheUuidStr
;
380 uuid_unparse(*closure
.dyldCacheUUID(), cacheUuidStr
);
381 root
.map
["dyld-cache-uuid"].value
= cacheUuidStr
;
383 // add top level images
384 Node
& rootImages
= root
.map
["root-images"];
385 uint32_t initImageCount
= closure
.mainExecutableImageIndex();
386 rootImages
.array
.resize(initImageCount
+1);
387 for (uint32_t i
=0; i
<= initImageCount
; ++i
) {
388 const Image image
= closure
.group().image(i
);
389 uuid_string_t uuidStr
;
390 uuid_unparse(image
->uuid(), uuidStr
);
391 rootImages
.array
[i
].value
= uuidStr
;
393 root
.map
["initial-image-count"].value
= decimal(closure
.initialImageCount());
396 root
.map
["group-num"].value
= decimal(closure
.group().groupNum());
399 __block Node cacheOverrides
;
400 closure
.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex
, uint32_t imageIndexInClosure
, uint32_t imageOffset
, bool& stop
) {
402 patch
.map
["patch-index"].value
= decimal(patchTableIndex
);
403 patch
.map
["replacement"].value
= "{closure[" + decimal(imageIndexInClosure
) + "]+" + hex(imageOffset
) + "}";
404 cacheOverrides
.array
.push_back(patch
);
406 if ( !cacheOverrides
.array
.empty() )
407 root
.map
["dyld-cache-overrides"].array
= cacheOverrides
.array
;
412 void printImageAsJSON(const Image
* image
, const Array
<const ImageArray
*>& imagesArrays
, bool printFixups
, FILE* out
)
414 Node root
= buildImageNode(image
, imagesArrays
, printFixups
, false);
415 printJSON(root
, 0, out
);
418 void printDyldCacheImagesAsJSON(const DyldSharedCache
* dyldCache
, bool printFixups
, FILE* out
)
420 const dyld3::closure::ImageArray
* dylibs
= dyldCache
->cachedDylibsImageArray();
421 STACK_ALLOC_ARRAY(const ImageArray
*, imagesArrays
, 2);
422 imagesArrays
.push_back(dylibs
);
424 Node root
= buildImageArrayNode(dylibs
, imagesArrays
, printFixups
, false, (uint8_t*)dyldCache
);
425 printJSON(root
, 0, out
);
428 void printClosureAsJSON(const LaunchClosure
* cls
, const Array
<const ImageArray
*>& imagesArrays
, bool printFixups
, FILE* out
)
430 Node root
= buildClosureNode(cls
, imagesArrays
, printFixups
, false);
431 printJSON(root
, 0, out
);
434 void printClosureAsJSON(const DlopenClosure
* cls
, const Array
<const ImageArray
*>& imagesArrays
, bool printFixups
, FILE* out
)
436 Node root
= buildClosureNode(cls
, imagesArrays
, printFixups
, false);
437 printJSON(root
, 0, out
);
441 } // namespace closure