]> git.saurik.com Git - apple/dyld.git/blob - dyld3/LaunchCachePrinter.cpp
dyld-551.4.tar.gz
[apple/dyld.git] / dyld3 / LaunchCachePrinter.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
25 #include <string.h>
26
27 #include <string>
28 #include <map>
29 #include <vector>
30
31 #include "LaunchCache.h"
32 #include "LaunchCacheFormat.h"
33
34 #if !DYLD_IN_PROCESS
35
36 namespace dyld3 {
37 namespace launch_cache {
38
39 struct Node
40 {
41 std::string value;
42 std::map<std::string, Node> map;
43 std::vector<Node> array;
44 };
45
46 static std::string hex(uint64_t value) {
47 char buff[64];
48 sprintf(buff, "0x%llX", value);
49 return buff;
50 }
51
52 static std::string hex5(uint64_t value) {
53 char buff[64];
54 sprintf(buff, "0x%05llX", value);
55 return buff;
56 }
57
58 static std::string decimal(uint64_t value) {
59 char buff[64];
60 sprintf(buff, "%llu", value);
61 return buff;
62 }
63
64 static Node buildImageNode(const Image& image, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
65 {
66 __block Node imageNode;
67
68 if ( image.isInvalid() )
69 return imageNode;
70
71 const ImageGroup group = image.group();
72 imageNode.map["path"].value = image.path();
73 __block Node imageAliases;
74 group.forEachAliasOf(group.indexInGroup(image.binaryData()), ^(const char* aliasPath, uint32_t aliasPathHash, bool& stop) {
75 Node anAlias;
76 anAlias.value = aliasPath;
77 imageAliases.array.push_back(anAlias);
78 });
79 if ( !imageAliases.array.empty() )
80 imageNode.map["aliases"] = imageAliases;
81 uuid_string_t uuidStr;
82 uuid_unparse(*image.uuid(), uuidStr);
83 imageNode.map["uuid"].value = uuidStr;
84 imageNode.map["has-objc"].value = (image.hasObjC() ? "true" : "false");
85 imageNode.map["has-weak-defs"].value = (image.hasWeakDefs() ? "true" : "false");
86 imageNode.map["never-unload"].value = (image.neverUnload() ? "true" : "false");
87 imageNode.map["platform-binary"].value = (image.isPlatformBinary() ? "true" : "false");
88 if ( group.groupNum() == 0 )
89 imageNode.map["overridable-dylib"].value = (image.overridableDylib() ? "true" : "false");
90 if ( image.cwdMustBeThisDir() )
91 imageNode.map["cwd-must-be-this-dir"].value = "true";
92 if ( image.isDiskImage() ) {
93 uint32_t csFileOffset;
94 uint32_t csSize;
95 if ( image.hasCodeSignature(csFileOffset, csSize) ) {
96 imageNode.map["code-sign-location"].map["offset"].value = hex(csFileOffset);
97 imageNode.map["code-sign-location"].map["size"].value = hex(csSize);
98 }
99 uint32_t fpTextOffset;
100 uint32_t fpSize;
101 if ( image.isFairPlayEncrypted(fpTextOffset, fpSize) ) {
102 imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset);
103 imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize);
104 }
105 if ( image.validateUsingModTimeAndInode() ) {
106 imageNode.map["file-mod-time"].value = hex(image.fileModTime());
107 imageNode.map["file-inode"].value = hex(image.fileINode());
108 }
109 else {
110 const uint8_t* cdHash = image.cdHash16();
111 std::string cdHashStr;
112 cdHashStr.reserve(32);
113 for (int j=0; j < 16; ++j) {
114 uint8_t byte = cdHash[j];
115 uint8_t nibbleL = byte & 0x0F;
116 uint8_t nibbleH = byte >> 4;
117 if ( nibbleH < 10 )
118 cdHashStr += '0' + nibbleH;
119 else
120 cdHashStr += 'a' + (nibbleH-10);
121 if ( nibbleL < 10 )
122 cdHashStr += '0' + nibbleL;
123 else
124 cdHashStr += 'a' + (nibbleL-10);
125 }
126 imageNode.map["file-cd-hash-16"].value = cdHashStr;
127 }
128 imageNode.map["total-vm-size"].value = hex(image.vmSizeToMap());
129 uint64_t sliceOffset = image.sliceOffsetInFile();
130 if ( sliceOffset != 0 )
131 imageNode.map["file-offset-of-slice"].value = hex(sliceOffset);
132 if ( image.hasTextRelocs() )
133 imageNode.map["has-text-relocs"].value = "true";
134 image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
135 Node segInfoNode;
136 segInfoNode.map["file-offset"].value = hex(fileOffset);
137 segInfoNode.map["file-size"].value = hex(fileSize);
138 segInfoNode.map["vm-size"].value = hex(vmSize);
139 segInfoNode.map["permissions"].value = hex(permissions);
140 imageNode.map["mappings"].array.push_back(segInfoNode);
141 });
142 if ( printFixups ) {
143 image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) {
144 MemoryRange segContent = { nullptr, vmSize };
145 std::string segName = "segment-" + decimal(segIndex);
146 __block Node segmentFixupsNode;
147 image.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) {
148 switch ( kind ) {
149 case Image::FixupKind::rebase32:
150 segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit rebase";
151 break;
152 case Image::FixupKind::rebase64:
153 segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "64-bit rebase";
154 break;
155 case Image::FixupKind::rebaseText32 :
156 segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit text rebase";
157 break;
158 case Image::FixupKind::bind32:
159 segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit bind, target=") + value.asString(group);
160 break;
161 case Image::FixupKind::bind64:
162 segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("64-bit bind, target=") + value.asString(group);
163 break;
164 case Image::FixupKind::bindText32 :
165 segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text abs bind, target=") + value.asString(group);
166 break;
167 case Image::FixupKind::bindTextRel32 :
168 segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text rel bind, target=") + value.asString(group);
169 break;
170 case Image::FixupKind::bindImportJmp32 :
171 segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit IMPORT JMP rel bind, target=") + value.asString(group);
172 break;
173 }
174 });
175 if ( segmentFixupsNode.map[segName].map.size() != 0 ) {
176 imageNode.map["fixups"].array.push_back(segmentFixupsNode);
177 }
178 });
179 }
180 }
181 else {
182 imageNode.map["patch-start-index"].value = decimal(image.patchStartIndex());
183 imageNode.map["patch-count"].value = decimal(image.patchCount());
184 }
185
186 // add dependents
187 image.forEachDependentImage(groupList, ^(uint32_t depIndex, Image depImage, Image::LinkKind kind, bool& stop) {
188 Node depMapNode;
189 depMapNode.map["path"].value = depImage.path();
190 if ( printDependentsDetails ) {
191 ImageGroup depGroup = depImage.group();
192 uint32_t indexInGroup = depGroup.indexInGroup(depImage.binaryData());
193 depMapNode.map["group-index"].value = decimal(depGroup.groupNum());
194 depMapNode.map["index-in-group"].value = decimal(indexInGroup);
195 }
196 switch ( kind ) {
197 case Image::LinkKind::regular:
198 depMapNode.map["link"].value = "regular";
199 break;
200 case Image::LinkKind::reExport:
201 depMapNode.map["link"].value = "re-export";
202 break;
203 case Image::LinkKind::upward:
204 depMapNode.map["link"].value = "upward";
205 break;
206 case Image::LinkKind::weak:
207 depMapNode.map["link"].value = "weak";
208 break;
209 }
210 imageNode.map["dependents"].array.push_back(depMapNode);
211 });
212 // add things to init before this image
213 __block Node initBeforeNode;
214 image.forEachInitBefore(groupList, ^(Image beforeImage) {
215 Node beforeNode;
216 beforeNode.value = beforeImage.path();
217 imageNode.map["initializer-order"].array.push_back(beforeNode);
218 });
219
220 // add initializers
221 image.forEachInitializer(nullptr, ^(const void* initializer) {
222 Node initNode;
223 initNode.value = hex((long)initializer);
224 imageNode.map["initializer-offsets"].array.push_back(initNode);
225 });
226
227 // add override info if relevant
228 group.forEachImageRefOverride(groupList, ^(Image standardDylib, Image overrideDylib, bool& stop) {
229 if ( overrideDylib.binaryData() == image.binaryData() ) {
230 imageNode.map["override-of-cached-dylib"].value = standardDylib.path();
231 }
232 });
233
234 // add dtrace info
235 image.forEachDOF(nullptr, ^(const void* section) {
236 Node initNode;
237 initNode.value = hex((long)section);
238 imageNode.map["dof-offsets"].array.push_back(initNode);
239 });
240
241 return imageNode;
242 }
243
244
245 static Node buildImageGroupNode(const ImageGroup& group, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
246 {
247 Node images;
248 uint32_t imageCount = group.imageCount();
249 images.array.reserve(imageCount);
250 for (uint32_t i=0; i < imageCount; ++i) {
251 images.array.push_back(buildImageNode(group.image(i), groupList, printFixups, printDependentsDetails));
252 }
253 return images;
254 }
255
256 static Node buildClosureNode(const Closure& closure, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
257 {
258 __block Node root;
259
260 // add env-vars if they exist
261 closure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
262 const char* equ = strchr(keyEqualValue, '=');
263 if ( equ != nullptr ) {
264 char key[512];
265 strncpy(key, keyEqualValue, equ-keyEqualValue);
266 key[equ-keyEqualValue] = '\0';
267 root.map["env-vars"].map[key].value = equ+1;
268 }
269 });
270
271 // add missing files array if they exist
272 closure.forEachMustBeMissingFile(^(const char* path, bool& stop) {
273 Node fileNode;
274 fileNode.value = path;
275 root.map["must-be-missing-files"].array.push_back(fileNode);
276 });
277
278 const uint8_t* cdHash = closure.cdHash();
279 std::string cdHashStr;
280 cdHashStr.reserve(24);
281 for (int i=0; i < 20; ++i) {
282 uint8_t byte = cdHash[i];
283 uint8_t nibbleL = byte & 0x0F;
284 uint8_t nibbleH = byte >> 4;
285 if ( nibbleH < 10 )
286 cdHashStr += '0' + nibbleH;
287 else
288 cdHashStr += 'a' + (nibbleH-10);
289 if ( nibbleL < 10 )
290 cdHashStr += '0' + nibbleL;
291 else
292 cdHashStr += 'a' + (nibbleL-10);
293 }
294 if ( cdHashStr != "0000000000000000000000000000000000000000" )
295 root.map["cd-hash"].value = cdHashStr;
296
297 // add uuid of dyld cache this closure requires
298 closure.dyldCacheUUID();
299 uuid_string_t cacheUuidStr;
300 uuid_unparse(*closure.dyldCacheUUID(), cacheUuidStr);
301 root.map["dyld-cache-uuid"].value = cacheUuidStr;
302
303 // add top level images
304 Node& rootImages = root.map["root-images"];
305 uint32_t initImageCount = closure.mainExecutableImageIndex();
306 rootImages.array.resize(initImageCount+1);
307 for (uint32_t i=0; i <= initImageCount; ++i) {
308 const Image image = closure.group().image(i);
309 uuid_string_t uuidStr;
310 uuid_unparse(*image.uuid(), uuidStr);
311 rootImages.array[i].value = uuidStr;
312 }
313 root.map["initial-image-count"].value = decimal(closure.initialImageCount());
314
315 // add images
316 root.map["images"] = buildImageGroupNode(closure.group(), groupList, printFixups, printDependentsDetails);
317 root.map["group-num"].value = decimal(closure.group().groupNum());
318
319 if ( closure.mainExecutableUsesCRT() )
320 root.map["main-offset"].value = hex(closure.mainExecutableEntryOffset());
321 else
322 root.map["start-offset"].value = hex(closure.mainExecutableEntryOffset());
323
324 root.map["libdyld-entry-offset"].value = hex(closure.libdyldVectorOffset());
325
326 root.map["restricted"].value = (closure.isRestricted() ? "true" : "false");
327
328 root.map["library-validation"].value = (closure.usesLibraryValidation() ? "true" : "false");
329
330 __block Node cacheOverrides;
331 closure.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop) {
332 Node patch;
333 patch.map["patch-index"].value = decimal(patchTableIndex);
334 patch.map["replacement"].value = "{closure[" + decimal(imageIndexInClosure) + "]+" + hex(imageOffset) + "}";
335 cacheOverrides.array.push_back(patch);
336 });
337 if ( !cacheOverrides.array.empty() )
338 root.map["dyld-cache-overrides"].array = cacheOverrides.array;
339
340 return root;
341 }
342
343 static void indentBy(uint32_t spaces, FILE* out) {
344 for (int i=0; i < spaces; ++i) {
345 fprintf(out, " ");
346 }
347 }
348
349 static void printJSON(const Node& node, uint32_t indent, FILE* out)
350 {
351 if ( !node.map.empty() ) {
352 fprintf(out, "{");
353 bool needComma = false;
354 for (const auto& entry : node.map) {
355 if ( needComma )
356 fprintf(out, ",");
357 fprintf(out, "\n");
358 indentBy(indent+2, out);
359 fprintf(out, "\"%s\": ", entry.first.c_str());
360 printJSON(entry.second, indent+2, out);
361 needComma = true;
362 }
363 fprintf(out, "\n");
364 indentBy(indent, out);
365 fprintf(out, "}");
366 }
367 else if ( !node.array.empty() ) {
368 fprintf(out, "[");
369 bool needComma = false;
370 for (const auto& entry : node.array) {
371 if ( needComma )
372 fprintf(out, ",");
373 fprintf(out, "\n");
374 indentBy(indent+2, out);
375 printJSON(entry, indent+2, out);
376 needComma = true;
377 }
378 fprintf(out, "\n");
379 indentBy(indent, out);
380 fprintf(out, "]");
381 }
382 else {
383 fprintf(out, "\"%s\"", node.value.c_str());
384 }
385 if ( indent == 0 )
386 fprintf(out, "\n");
387 }
388
389
390 void Image::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
391 {
392 Node image = buildImageNode(*this, groupList, printFixups, printDependentsDetails);
393 printJSON(image, 0, out);
394 }
395
396 void ImageGroup::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
397 {
398 Node root;
399 root.map["images"] = buildImageGroupNode(*this, groupList, printFixups, printDependentsDetails);
400 root.map["group-num"].value = decimal(groupNum());
401 root.map["dylibs-expected-on-disk"].value = (dylibsExpectedOnDisk() ? "true" : "false");
402 printJSON(root, 0, out);
403 }
404
405 void ImageGroup::printStatistics(FILE* out) const
406 {
407 __block uint32_t totalRebases = 0;
408 __block uint32_t totalBinds = 0;
409 for (uint32_t i=0; i < imageCount(); ++i) {
410 Image img(image(i));
411 img.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) {
412 MemoryRange segContent = { nullptr, vmSize };
413 img.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) {
414 if ( kind == Image::FixupKind::rebase64 )
415 ++totalRebases;
416 else
417 ++totalBinds;
418 });
419 });
420 }
421
422 fprintf(out, "ImageGroup:\n");
423 fprintf(out, " image-count: % 5d\n", _binaryData->imagesPoolCount);
424 fprintf(out, " alias-count: % 5d\n", _binaryData->imageAliasCount);
425 fprintf(out, " segments-count: % 5d\n", _binaryData->segmentsPoolCount);
426 fprintf(out, " dependents-count: % 5d\n", _binaryData->dependentsPoolCount);
427 fprintf(out, " targets-count: % 5d\n", _binaryData->targetsPoolCount);
428 fprintf(out, " rebase-count: % 5d\n", totalRebases);
429 fprintf(out, " bind-count: % 5d\n", totalBinds);
430 fprintf(out, " fixups-size: % 8d bytes\n", _binaryData->fixupsPoolSize);
431 fprintf(out, " targets-size: % 8ld bytes\n", _binaryData->targetsPoolCount * sizeof(uint64_t));
432 fprintf(out, " strings-size: % 8d bytes\n", _binaryData->stringsPoolSize);
433 fprintf(out, " dofs-size: % 8ld bytes\n", _binaryData->dofOffsetPoolCount * sizeof(uint32_t));
434 fprintf(out, " indirect-groups-size: % 8ld bytes\n", _binaryData->indirectGroupNumPoolCount * sizeof(uint32_t));
435 }
436
437
438 void Closure::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
439 {
440 Node root = buildClosureNode(*this, groupList, printFixups, printDependentsDetails);
441 printJSON(root, 0, out);
442 }
443
444 void Closure::printStatistics(FILE* out) const
445 {
446 fprintf(out, "closure size: %lu\n", size());
447 group().printStatistics(out);
448 }
449
450
451
452 } // namespace launch_cache
453 } // namespace dyld3
454
455 #endif
456
457