dyld-640.2.tar.gz
[apple/dyld.git] / dyld3 / ClosurePrinter.cpp
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <string.h>
25
26 #include <string>
27 #include <map>
28 #include <vector>
29
30 #include "ClosurePrinter.h"
31 #include "JSONWriter.h"
32
33 using namespace dyld3::json;
34
35 namespace dyld3 {
36 namespace closure {
37
38 static std::string printTarget(const Array<const ImageArray*>& imagesArrays, Image::ResolvedSymbolTarget target)
39 {
40 const Image* targetImage;
41 uint64_t value;
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);
48 }
49 else
50 return std::string("bind to ") + targetImage->leafName() + " + " + hex8(target.image.offset);
51 break;
52 case Image::ResolvedSymbolTarget::kindSharedCache:
53 return std::string("bind to dyld cache + ") + hex8(target.sharedCache.offset);
54 break;
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);
60 break;
61 }
62 return "???";
63 }
64
65
66 static Node buildImageNode(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr)
67 {
68 __block Node imageNode;
69
70 if ( image->isInvalid() )
71 return imageNode;
72
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) {
77 Node anAlias;
78 anAlias.value = aliasPath;
79 imageAliases.array.push_back(anAlias);
80 });
81 if ( !imageAliases.array.empty() )
82 imageNode.map["aliases"] = imageAliases;
83 uuid_t uuid;
84 if ( image->getUuid(uuid) ) {
85 uuid_string_t uuidStr;
86 uuid_unparse(uuid, uuidStr);
87 imageNode.map["uuid"].value = uuidStr;
88 }
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;
98 uint32_t csSize;
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);
102 }
103 // uint32_t fpTextOffset;
104 // uint32_t fpSize;
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);
108 // }
109 uint64_t inode;
110 uint64_t mTime;
111 if ( image->hasFileModTimeAndInode(inode, mTime) ) {
112 imageNode.map["file-mod-time"].value = hex(inode);
113 imageNode.map["file-inode"].value = hex(mTime);
114 }
115 uint8_t cdHash[20];
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;
123 if ( nibbleH < 10 )
124 cdHashStr += '0' + nibbleH;
125 else
126 cdHashStr += 'a' + (nibbleH-10);
127 if ( nibbleL < 10 )
128 cdHashStr += '0' + nibbleL;
129 else
130 cdHashStr += 'a' + (nibbleL-10);
131 }
132 if ( cdHashStr != "0000000000000000000000000000000000000000" )
133 imageNode.map["cd-hash"].value = cdHashStr;
134 }
135 else {
136 #if 0
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;
144 if ( nibbleH < 10 )
145 cdHashStr += '0' + nibbleH;
146 else
147 cdHashStr += 'a' + (nibbleH-10);
148 if ( nibbleL < 10 )
149 cdHashStr += '0' + nibbleL;
150 else
151 cdHashStr += 'a' + (nibbleL-10);
152 }
153 imageNode.map["file-cd-hash-16"].value = cdHashStr;
154 #endif
155 }
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) {
163 Node segInfoNode;
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);
169 });
170
171
172
173 if ( printFixups ) {
174 image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
175 // rebase
176 imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "rebase";
177 }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
178 // bind
179 imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
180 }, ^(uint64_t imageOffsetStart, const Array<Image::ResolvedSymbolTarget>& targets, bool& stop) {
181 // chain
182 imageNode.map["fixups"].map[hex8(imageOffsetStart)].value = "chain-start";
183 for (const Image::ResolvedSymbolTarget& target: targets) {
184 Node targetNode;
185 targetNode.value = printTarget(imagesArrays, target);
186 imageNode.map["fixups-targets"].array.push_back(targetNode);
187 }
188 });
189 image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool &stop) {
190 // rebase
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);
194 });
195 }
196 }
197 else {
198 if ( printFixups ) {
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) {
204 Node siteNode;
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);
212 }
213 implNode.map["usage-sites"].array.push_back(siteNode);
214 });
215 imageNode.map["patches"].array.push_back(implNode);
216 });
217 }
218 }
219
220 // add dependents
221 image->forEachDependentImage(^(uint32_t depIndex, Image::LinkKind kind, ImageNum imageNum, bool& stop) {
222 Node depMapNode;
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();
227 switch ( kind ) {
228 case Image::LinkKind::regular:
229 depMapNode.map["link"].value = "regular";
230 break;
231 case Image::LinkKind::reExport:
232 depMapNode.map["link"].value = "re-export";
233 break;
234 case Image::LinkKind::upward:
235 depMapNode.map["link"].value = "upward";
236 break;
237 case Image::LinkKind::weak:
238 depMapNode.map["link"].value = "weak";
239 break;
240 }
241 imageNode.map["dependents"].array.push_back(depMapNode);
242 });
243
244 // add initializers
245 image->forEachInitializer(nullptr, ^(const void* initializer) {
246 Node initNode;
247 initNode.value = hex((long)initializer);
248 imageNode.map["initializer-offsets"].array.push_back(initNode);
249 });
250
251 __block Node initBeforeNode;
252 image->forEachImageToInitBefore(^(ImageNum imageToInit, bool& stop) {
253 Node beforeNode;
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);
258 });
259
260 ImageNum cacheImageNum;
261 if ( image->isOverrideOfDyldCacheImage(cacheImageNum) ) {
262 imageNode.map["override-of-dyld-cache-image"].value = ImageArray::findImage(imagesArrays, cacheImageNum)->path();
263 }
264
265
266 #if 0
267 // add things to init before this image
268 __block Node initBeforeNode;
269 image->forEachInitBefore(groupList, ^(Image beforeImage) {
270 Node beforeNode;
271 beforeNode.value = beforeimage->path();
272 imageNode.map["initializer-order"].array.push_back(beforeNode);
273 });
274
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();
279 }
280 });
281 // add dtrace info
282 image->forEachDOF(nullptr, ^(const void* section) {
283 Node initNode;
284 initNode.value = hex((long)section);
285 imageNode.map["dof-offsets"].array.push_back(initNode);
286 });
287 #endif
288
289 return imageNode;
290 }
291
292
293 static Node buildImageArrayNode(const ImageArray* imageArray, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr)
294 {
295 __block Node images;
296 imageArray->forEachImage(^(const Image* image, bool& stop) {
297 images.array.push_back(buildImageNode(image, imagesArrays, printFixups, printDependentsDetails, cacheStart));
298 });
299 return images;
300 }
301
302
303 static Node buildClosureNode(const DlopenClosure* closure, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails)
304 {
305 __block Node root;
306 root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails);
307
308 closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
309 Node patchNode;
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);
314 });
315
316 return root;
317 }
318
319 static Node buildClosureNode(const LaunchClosure* closure, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails)
320 {
321 __block Node root;
322 root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails);
323
324 closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
325 Node patchNode;
326 patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
327 patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
328 patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
329 root.map["dyld-cache-fixups"].array.push_back(patchNode);
330 });
331
332 Image::ResolvedSymbolTarget entry;
333 if ( closure->mainEntry(entry) )
334 root.map["main"].value = printTarget(imagesArrays, entry);
335 else if ( closure->startEntry(entry) )
336 root.map["start"].value = printTarget(imagesArrays, entry);
337
338 Image::ResolvedSymbolTarget libdyldEntry;
339 closure->libDyldEntry(libdyldEntry);
340 root.map["libdyld-entry"].value = printTarget(imagesArrays, libdyldEntry);
341
342 root.map["uses-@paths"].value = (closure->usedAtPaths() ? "true" : "false");
343 root.map["uses-fallback-paths"].value = (closure->usedFallbackPaths() ? "true" : "false");
344
345 // add missing files array if they exist
346 closure->forEachMustBeMissingFile(^(const char* path, bool& stop) {
347 Node fileNode;
348 fileNode.value = path;
349 root.map["must-be-missing-files"].array.push_back(fileNode);
350 });
351
352 // add interposing info, if any
353 closure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool& stop) {
354 Node tupleNode;
355 tupleNode.map["stock"].value = printTarget(imagesArrays, tuple.stockImplementation);
356 tupleNode.map["replace"].value = printTarget(imagesArrays, tuple.newImplementation);
357 root.map["interposing-tuples"].array.push_back(tupleNode);
358 });
359
360 closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
361 Node patchNode;
362 patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
363 patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
364 patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
365 root.map["dyld-cache-fixups"].array.push_back(patchNode);
366 });
367
368 root.map["initial-image-count"].value = decimal(closure->initialLoadCount());
369
370 #if 0
371
372
373 // add env-vars if they exist
374 closure->forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
375 const char* equ = strchr(keyEqualValue, '=');
376 if ( equ != nullptr ) {
377 char key[512];
378 strncpy(key, keyEqualValue, equ-keyEqualValue);
379 key[equ-keyEqualValue] = '\0';
380 root.map["env-vars"].map[key].value = equ+1;
381 }
382 });
383
384
385 // add uuid of dyld cache this closure requires
386 closure.dyldCacheUUID();
387 uuid_string_t cacheUuidStr;
388 uuid_unparse(*closure.dyldCacheUUID(), cacheUuidStr);
389 root.map["dyld-cache-uuid"].value = cacheUuidStr;
390
391 // add top level images
392 Node& rootImages = root.map["root-images"];
393 uint32_t initImageCount = closure.mainExecutableImageIndex();
394 rootImages.array.resize(initImageCount+1);
395 for (uint32_t i=0; i <= initImageCount; ++i) {
396 const Image image = closure.group().image(i);
397 uuid_string_t uuidStr;
398 uuid_unparse(image->uuid(), uuidStr);
399 rootImages.array[i].value = uuidStr;
400 }
401 root.map["initial-image-count"].value = decimal(closure.initialImageCount());
402
403 // add images
404 root.map["group-num"].value = decimal(closure.group().groupNum());
405
406
407 __block Node cacheOverrides;
408 closure.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop) {
409 Node patch;
410 patch.map["patch-index"].value = decimal(patchTableIndex);
411 patch.map["replacement"].value = "{closure[" + decimal(imageIndexInClosure) + "]+" + hex(imageOffset) + "}";
412 cacheOverrides.array.push_back(patch);
413 });
414 if ( !cacheOverrides.array.empty() )
415 root.map["dyld-cache-overrides"].array = cacheOverrides.array;
416 #endif
417 return root;
418 }
419
420 void printImageAsJSON(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
421 {
422 Node root = buildImageNode(image, imagesArrays, printFixups, false);
423 printJSON(root, 0, out);
424 }
425
426 void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups, FILE* out)
427 {
428 const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
429 STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
430 imagesArrays.push_back(dylibs);
431
432 Node root = buildImageArrayNode(dylibs, imagesArrays, printFixups, false, (uint8_t*)dyldCache);
433 printJSON(root, 0, out);
434 }
435
436 void printClosureAsJSON(const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
437 {
438 Node root = buildClosureNode(cls, imagesArrays, printFixups, false);
439 printJSON(root, 0, out);
440 }
441
442 void printClosureAsJSON(const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
443 {
444 Node root = buildClosureNode(cls, imagesArrays, printFixups, false);
445 printJSON(root, 0, out);
446 }
447
448
449 } // namespace closure
450 } // namespace dyld3