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@
31 #include "LaunchCache.h"
32 #include "LaunchCacheFormat.h"
37 namespace launch_cache
{
42 std::map
<std::string
, Node
> map
;
43 std::vector
<Node
> array
;
46 static std::string
hex(uint64_t value
) {
48 sprintf(buff
, "0x%llX", value
);
52 static std::string
hex5(uint64_t value
) {
54 sprintf(buff
, "0x%05llX", value
);
58 static std::string
decimal(uint64_t value
) {
60 sprintf(buff
, "%llu", value
);
64 static Node
buildImageNode(const Image
& image
, const ImageGroupList
& groupList
, bool printFixups
, bool printDependentsDetails
)
66 __block Node imageNode
;
68 if ( image
.isInvalid() )
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
) {
76 anAlias
.value
= aliasPath
;
77 imageAliases
.array
.push_back(anAlias
);
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
;
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
);
99 uint32_t fpTextOffset
;
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
);
105 if ( image
.validateUsingModTimeAndInode() ) {
106 imageNode
.map
["file-mod-time"].value
= hex(image
.fileModTime());
107 imageNode
.map
["file-inode"].value
= hex(image
.fileINode());
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;
118 cdHashStr
+= '0' + nibbleH
;
120 cdHashStr
+= 'a' + (nibbleH
-10);
122 cdHashStr
+= '0' + nibbleL
;
124 cdHashStr
+= 'a' + (nibbleL
-10);
126 imageNode
.map
["file-cd-hash-16"].value
= cdHashStr
;
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
) {
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
);
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
) {
149 case Image::FixupKind::rebase32
:
150 segmentFixupsNode
.map
[segName
].map
[hex5(segOffset
)].value
= "32-bit rebase";
152 case Image::FixupKind::rebase64
:
153 segmentFixupsNode
.map
[segName
].map
[hex5(segOffset
)].value
= "64-bit rebase";
155 case Image::FixupKind::rebaseText32
:
156 segmentFixupsNode
.map
[segName
].map
[hex5(segOffset
)].value
= "32-bit text rebase";
158 case Image::FixupKind::bind32
:
159 segmentFixupsNode
.map
[segName
].map
[hex5(segOffset
)].value
= std::string("32-bit bind, target=") + value
.asString(group
);
161 case Image::FixupKind::bind64
:
162 segmentFixupsNode
.map
[segName
].map
[hex5(segOffset
)].value
= std::string("64-bit bind, target=") + value
.asString(group
);
164 case Image::FixupKind::bindText32
:
165 segmentFixupsNode
.map
[segName
].map
[hex5(segOffset
)].value
= std::string("32-bit text abs bind, target=") + value
.asString(group
);
167 case Image::FixupKind::bindTextRel32
:
168 segmentFixupsNode
.map
[segName
].map
[hex5(segOffset
)].value
= std::string("32-bit text rel bind, target=") + value
.asString(group
);
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
);
175 if ( segmentFixupsNode
.map
[segName
].map
.size() != 0 ) {
176 imageNode
.map
["fixups"].array
.push_back(segmentFixupsNode
);
182 imageNode
.map
["patch-start-index"].value
= decimal(image
.patchStartIndex());
183 imageNode
.map
["patch-count"].value
= decimal(image
.patchCount());
187 image
.forEachDependentImage(groupList
, ^(uint32_t depIndex
, Image depImage
, Image::LinkKind kind
, bool& stop
) {
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
);
197 case Image::LinkKind::regular
:
198 depMapNode
.map
["link"].value
= "regular";
200 case Image::LinkKind::reExport
:
201 depMapNode
.map
["link"].value
= "re-export";
203 case Image::LinkKind::upward
:
204 depMapNode
.map
["link"].value
= "upward";
206 case Image::LinkKind::weak
:
207 depMapNode
.map
["link"].value
= "weak";
210 imageNode
.map
["dependents"].array
.push_back(depMapNode
);
212 // add things to init before this image
213 __block Node initBeforeNode
;
214 image
.forEachInitBefore(groupList
, ^(Image beforeImage
) {
216 beforeNode
.value
= beforeImage
.path();
217 imageNode
.map
["initializer-order"].array
.push_back(beforeNode
);
221 image
.forEachInitializer(nullptr, ^(const void* initializer
) {
223 initNode
.value
= hex((long)initializer
);
224 imageNode
.map
["initializer-offsets"].array
.push_back(initNode
);
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();
235 image
.forEachDOF(nullptr, ^(const void* section
) {
237 initNode
.value
= hex((long)section
);
238 imageNode
.map
["dof-offsets"].array
.push_back(initNode
);
245 static Node
buildImageGroupNode(const ImageGroup
& group
, const ImageGroupList
& groupList
, bool printFixups
, bool printDependentsDetails
)
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
));
256 static Node
buildClosureNode(const Closure
& closure
, const ImageGroupList
& groupList
, bool printFixups
, bool printDependentsDetails
)
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 ) {
265 strncpy(key
, keyEqualValue
, equ
-keyEqualValue
);
266 key
[equ
-keyEqualValue
] = '\0';
267 root
.map
["env-vars"].map
[key
].value
= equ
+1;
271 // add missing files array if they exist
272 closure
.forEachMustBeMissingFile(^(const char* path
, bool& stop
) {
274 fileNode
.value
= path
;
275 root
.map
["must-be-missing-files"].array
.push_back(fileNode
);
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;
286 cdHashStr
+= '0' + nibbleH
;
288 cdHashStr
+= 'a' + (nibbleH
-10);
290 cdHashStr
+= '0' + nibbleL
;
292 cdHashStr
+= 'a' + (nibbleL
-10);
294 if ( cdHashStr
!= "0000000000000000000000000000000000000000" )
295 root
.map
["cd-hash"].value
= cdHashStr
;
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
;
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
;
313 root
.map
["initial-image-count"].value
= decimal(closure
.initialImageCount());
316 root
.map
["images"] = buildImageGroupNode(closure
.group(), groupList
, printFixups
, printDependentsDetails
);
317 root
.map
["group-num"].value
= decimal(closure
.group().groupNum());
319 if ( closure
.mainExecutableUsesCRT() )
320 root
.map
["main-offset"].value
= hex(closure
.mainExecutableEntryOffset());
322 root
.map
["start-offset"].value
= hex(closure
.mainExecutableEntryOffset());
324 root
.map
["libdyld-entry-offset"].value
= hex(closure
.libdyldVectorOffset());
326 root
.map
["restricted"].value
= (closure
.isRestricted() ? "true" : "false");
328 root
.map
["library-validation"].value
= (closure
.usesLibraryValidation() ? "true" : "false");
330 __block Node cacheOverrides
;
331 closure
.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex
, uint32_t imageIndexInClosure
, uint32_t imageOffset
, bool& stop
) {
333 patch
.map
["patch-index"].value
= decimal(patchTableIndex
);
334 patch
.map
["replacement"].value
= "{closure[" + decimal(imageIndexInClosure
) + "]+" + hex(imageOffset
) + "}";
335 cacheOverrides
.array
.push_back(patch
);
337 if ( !cacheOverrides
.array
.empty() )
338 root
.map
["dyld-cache-overrides"].array
= cacheOverrides
.array
;
343 static void indentBy(uint32_t spaces
, FILE* out
) {
344 for (int i
=0; i
< spaces
; ++i
) {
349 static void printJSON(const Node
& node
, uint32_t indent
, FILE* out
)
351 if ( !node
.map
.empty() ) {
353 bool needComma
= false;
354 for (const auto& entry
: node
.map
) {
358 indentBy(indent
+2, out
);
359 fprintf(out
, "\"%s\": ", entry
.first
.c_str());
360 printJSON(entry
.second
, indent
+2, out
);
364 indentBy(indent
, out
);
367 else if ( !node
.array
.empty() ) {
369 bool needComma
= false;
370 for (const auto& entry
: node
.array
) {
374 indentBy(indent
+2, out
);
375 printJSON(entry
, indent
+2, out
);
379 indentBy(indent
, out
);
383 fprintf(out
, "\"%s\"", node
.value
.c_str());
390 void Image::printAsJSON(const ImageGroupList
& groupList
, bool printFixups
, bool printDependentsDetails
, FILE* out
) const
392 Node image
= buildImageNode(*this, groupList
, printFixups
, printDependentsDetails
);
393 printJSON(image
, 0, out
);
396 void ImageGroup::printAsJSON(const ImageGroupList
& groupList
, bool printFixups
, bool printDependentsDetails
, FILE* out
) const
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
);
405 void ImageGroup::printStatistics(FILE* out
) const
407 __block
uint32_t totalRebases
= 0;
408 __block
uint32_t totalBinds
= 0;
409 for (uint32_t i
=0; i
< imageCount(); ++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
)
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));
438 void Closure::printAsJSON(const ImageGroupList
& groupList
, bool printFixups
, bool printDependentsDetails
, FILE* out
) const
440 Node root
= buildClosureNode(*this, groupList
, printFixups
, printDependentsDetails
);
441 printJSON(root
, 0, out
);
444 void Closure::printStatistics(FILE* out
) const
446 fprintf(out
, "closure size: %lu\n", size());
447 group().printStatistics(out
);
452 } // namespace launch_cache