1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 * Copyright (c) 2014 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
27 #include <sys/errno.h>
28 #include <sys/fcntl.h>
29 #include <sys/param.h>
30 #include <mach/mach.h>
31 #include <mach-o/loader.h>
32 #include <mach-o/fat.h>
33 #include <mach-o/dyld_priv.h>
38 #if BUILDING_CACHE_BUILDER
42 #include <unordered_map>
43 #include <unordered_set>
44 #include "SharedCacheBuilder.h"
45 #include "FileUtils.h"
49 #include "MachOLoaded.h"
50 #include "ClosureFileSystemPhysical.h"
51 #include "DyldSharedCache.h"
53 #include "StringUtils.h"
55 #include "objc-shared-cache.h"
57 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
58 #include "JSONWriter.h"
63 #if BUILDING_CACHE_BUILDER
64 DyldSharedCache::CreateResults
DyldSharedCache::create(const CreateOptions
& options
,
65 const dyld3::closure::FileSystem
& fileSystem
,
66 const std::vector
<MappedMachO
>& dylibsToCache
,
67 const std::vector
<MappedMachO
>& otherOsDylibs
,
68 const std::vector
<MappedMachO
>& osExecutables
)
70 CreateResults results
;
71 SharedCacheBuilder
cache(options
, fileSystem
);
72 if (!cache
.errorMessage().empty()) {
73 results
.errorMessage
= cache
.errorMessage();
77 std::vector
<FileAlias
> aliases
;
78 switch ( options
.platform
) {
79 case dyld3::Platform::iOS
:
80 case dyld3::Platform::watchOS
:
81 case dyld3::Platform::tvOS
:
82 // FIXME: embedded cache builds should be getting aliases from manifest
83 aliases
.push_back({"/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", "/System/Library/Frameworks/IOKit.framework/IOKit"});
84 aliases
.push_back({"/usr/lib/libstdc++.6.dylib", "/usr/lib/libstdc++.dylib"});
85 aliases
.push_back({"/usr/lib/libstdc++.6.dylib", "/usr/lib/libstdc++.6.0.9.dylib"});
86 aliases
.push_back({"/usr/lib/libz.1.dylib", "/usr/lib/libz.dylib"});
87 aliases
.push_back({"/usr/lib/libSystem.B.dylib", "/usr/lib/libSystem.dylib"});
88 aliases
.push_back({"/System/Library/Frameworks/Foundation.framework/Foundation", "/usr/lib/libextension.dylib"}); // <rdar://44315703>
94 cache
.build(dylibsToCache
, otherOsDylibs
, osExecutables
, aliases
);
96 results
.agileSignature
= cache
.agileSignature();
97 results
.cdHashFirst
= cache
.cdHashFirst();
98 results
.cdHashSecond
= cache
.cdHashSecond();
99 results
.warnings
= cache
.warnings();
100 results
.evictions
= cache
.evictions();
101 if ( cache
.errorMessage().empty() ) {
102 if ( !options
.outputFilePath
.empty() ) {
103 // write cache file, if path non-empty
104 cache
.writeFile(options
.outputFilePath
);
106 if ( !options
.outputMapFilePath
.empty() ) {
107 // write map file, if path non-empty
108 cache
.writeMapFile(options
.outputMapFilePath
);
111 results
.errorMessage
= cache
.errorMessage();
112 cache
.deleteBuffer();
116 bool DyldSharedCache::verifySelfContained(std::vector
<MappedMachO
>& dylibsToCache
,
117 std::unordered_set
<std::string
>& badZippered
,
118 MappedMachO (^loader
)(const std::string
& runtimePath
),
119 std::vector
<std::pair
<DyldSharedCache::MappedMachO
, std::set
<std::string
>>>& rejected
)
121 // build map of dylibs
122 __block
std::map
<std::string
, std::set
<std::string
>> badDylibs
;
123 __block
std::set
<std::string
> knownDylibs
;
124 for (const DyldSharedCache::MappedMachO
& dylib
: dylibsToCache
) {
125 std::set
<std::string
> reasons
;
126 if ( dylib
.mh
->canBePlacedInDyldCache(dylib
.runtimePath
.c_str(), ^(const char* msg
) { badDylibs
[dylib
.runtimePath
].insert(msg
);}) ) {
127 knownDylibs
.insert(dylib
.runtimePath
);
128 knownDylibs
.insert(dylib
.mh
->installName());
130 badDylibs
[dylib
.runtimePath
].insert("");
134 // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
135 __block
bool doAgain
= true;
137 __block
std::vector
<DyldSharedCache::MappedMachO
> foundMappings
;
139 // scan dylib list making sure all dependents are in dylib list
140 for (const DyldSharedCache::MappedMachO
& dylib
: dylibsToCache
) {
141 if ( badDylibs
.count(dylib
.runtimePath
) != 0 )
143 dylib
.mh
->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
144 if ( knownDylibs
.count(loadPath
) == 0 ) {
146 if ( badZippered
.count(loadPath
) != 0 ) {
147 badDylibs
[dylib
.runtimePath
].insert("");
148 knownDylibs
.erase(dylib
.runtimePath
);
149 knownDylibs
.erase(dylib
.mh
->installName());
150 badZippered
.insert(dylib
.runtimePath
);
151 badZippered
.insert(dylib
.mh
->installName());
154 MappedMachO foundMapping
;
155 if ( badDylibs
.count(loadPath
) == 0 )
156 foundMapping
= loader(loadPath
);
157 if ( foundMapping
.length
== 0 ) {
158 badDylibs
[dylib
.runtimePath
].insert(std::string("Could not find dependency '") + loadPath
+"'");
159 knownDylibs
.erase(dylib
.runtimePath
);
160 knownDylibs
.erase(dylib
.mh
->installName());
163 std::set
<std::string
> reasons
;
164 if ( foundMapping
.mh
->canBePlacedInDyldCache(foundMapping
.runtimePath
.c_str(), ^(const char* msg
) { badDylibs
[foundMapping
.runtimePath
].insert(msg
);})) {
165 // see if existing mapping was returned
166 bool alreadyInVector
= false;
167 for (const MappedMachO
& existing
: dylibsToCache
) {
168 if ( existing
.mh
== foundMapping
.mh
) {
169 alreadyInVector
= true;
173 if ( !alreadyInVector
)
174 foundMappings
.push_back(foundMapping
);
175 knownDylibs
.insert(loadPath
);
176 knownDylibs
.insert(foundMapping
.runtimePath
);
177 knownDylibs
.insert(foundMapping
.mh
->installName());
179 badDylibs
[dylib
.runtimePath
].insert("");
185 dylibsToCache
.insert(dylibsToCache
.end(), foundMappings
.begin(), foundMappings
.end());
187 const auto badDylibsCopy
= badDylibs
;
188 dylibsToCache
.erase(std::remove_if(dylibsToCache
.begin(), dylibsToCache
.end(), [&](const DyldSharedCache::MappedMachO
& dylib
) {
189 auto i
= badDylibsCopy
.find(dylib
.runtimePath
);
190 if ( i
!= badDylibsCopy
.end()) {
191 // Only add the warning if we are not a bad zippered dylib
192 if ( badZippered
.count(dylib
.runtimePath
) == 0 )
193 rejected
.push_back(std::make_pair(dylib
, i
->second
));
199 }), dylibsToCache
.end());
202 return badDylibs
.empty();
207 const T
DyldSharedCache::getAddrField(uint64_t addr
) const {
208 uint64_t slide
= (uint64_t)this - unslidLoadAddress();
209 return (const T
)(addr
+ slide
);
212 void DyldSharedCache::forEachRegion(void (^handler
)(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
)) const
214 // <rdar://problem/49875993> sanity check cache header
215 if ( strncmp(header
.magic
, "dyld_v1", 7) != 0 )
217 if ( header
.mappingOffset
> 1024 )
219 if ( header
.mappingCount
> 20 )
221 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
222 const dyld_cache_mapping_info
* mappingsEnd
= &mappings
[header
.mappingCount
];
223 for (const dyld_cache_mapping_info
* m
=mappings
; m
< mappingsEnd
; ++m
) {
224 handler((char*)this + m
->fileOffset
, m
->address
, m
->size
, m
->initProt
);
228 bool DyldSharedCache::inCache(const void* addr
, size_t length
, bool& readOnly
) const
230 // quick out if before start of cache
234 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
235 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
236 uintptr_t unslidStart
= (uintptr_t)addr
- slide
;
238 // quick out if after end of cache
239 if ( unslidStart
> (mappings
[2].address
+ mappings
[2].size
) )
242 // walk cache regions
243 const dyld_cache_mapping_info
* mappingsEnd
= &mappings
[header
.mappingCount
];
244 uintptr_t unslidEnd
= unslidStart
+ length
;
245 for (const dyld_cache_mapping_info
* m
=mappings
; m
< mappingsEnd
; ++m
) {
246 if ( (unslidStart
>= m
->address
) && (unslidEnd
< (m
->address
+m
->size
)) ) {
247 readOnly
= ((m
->initProt
& VM_PROT_WRITE
) == 0);
255 void DyldSharedCache::forEachImage(void (^handler
)(const mach_header
* mh
, const char* installName
)) const
257 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
258 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
259 if ( mappings
[0].fileOffset
!= 0 )
261 uint64_t firstImageOffset
= 0;
262 uint64_t firstRegionAddress
= mappings
[0].address
;
263 for (uint32_t i
=0; i
< header
.imagesCount
; ++i
) {
264 const char* dylibPath
= (char*)this + dylibs
[i
].pathFileOffset
;
265 uint64_t offset
= dylibs
[i
].address
- firstRegionAddress
;
266 if ( firstImageOffset
== 0 )
267 firstImageOffset
= offset
;
269 if ( dylibs
[i
].pathFileOffset
< firstImageOffset
)
271 const mach_header
* mh
= (mach_header
*)((char*)this + offset
);
272 handler(mh
, dylibPath
);
276 void DyldSharedCache::forEachImageEntry(void (^handler
)(const char* path
, uint64_t mTime
, uint64_t inode
)) const
278 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
279 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
280 if ( mappings
[0].fileOffset
!= 0 )
282 uint64_t firstImageOffset
= 0;
283 uint64_t firstRegionAddress
= mappings
[0].address
;
284 for (uint32_t i
=0; i
< header
.imagesCount
; ++i
) {
285 const char* dylibPath
= (char*)this + dylibs
[i
].pathFileOffset
;
286 uint64_t offset
= dylibs
[i
].address
- firstRegionAddress
;
287 if ( firstImageOffset
== 0 )
288 firstImageOffset
= offset
;
290 if ( dylibs
[i
].pathFileOffset
< firstImageOffset
)
292 handler(dylibPath
, dylibs
[i
].modTime
, dylibs
[i
].inode
);
296 const mach_header
* DyldSharedCache::getIndexedImageEntry(uint32_t index
, uint64_t& mTime
, uint64_t& inode
) const
298 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
299 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
300 mTime
= dylibs
[index
].modTime
;
301 inode
= dylibs
[index
].inode
;
302 return (mach_header
*)((uint8_t*)this + dylibs
[index
].address
- mappings
[0].address
);
305 void DyldSharedCache::forEachImageTextSegment(void (^handler
)(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const uuid_t dylibUUID
, const char* installName
, bool& stop
)) const
307 // check for old cache without imagesText array
308 if ( header
.mappingOffset
< 123 )
311 // walk imageText table and call callback for each entry
312 const dyld_cache_image_text_info
* imagesText
= (dyld_cache_image_text_info
*)((char*)this + header
.imagesTextOffset
);
313 const dyld_cache_image_text_info
* imagesTextEnd
= &imagesText
[header
.imagesTextCount
];
315 for (const dyld_cache_image_text_info
* p
=imagesText
; p
< imagesTextEnd
&& !stop
; ++p
) {
316 handler(p
->loadAddress
, p
->textSegmentSize
, p
->uuid
, (char*)this + p
->pathOffset
, stop
);
320 bool DyldSharedCache::addressInText(uint32_t cacheOffset
, uint32_t* imageIndex
) const
322 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
323 if ( cacheOffset
> mappings
[0].size
)
325 uint64_t targetAddr
= mappings
[0].address
+ cacheOffset
;
326 // walk imageText table and call callback for each entry
327 const dyld_cache_image_text_info
* imagesText
= (dyld_cache_image_text_info
*)((char*)this + header
.imagesTextOffset
);
328 const dyld_cache_image_text_info
* imagesTextEnd
= &imagesText
[header
.imagesTextCount
];
329 for (const dyld_cache_image_text_info
* p
=imagesText
; p
< imagesTextEnd
; ++p
) {
330 if ( (p
->loadAddress
<= targetAddr
) && (targetAddr
< p
->loadAddress
+p
->textSegmentSize
) ) {
331 *imageIndex
= (uint32_t)(p
-imagesText
);
338 const char* DyldSharedCache::archName() const
340 const char* archSubString
= ((char*)this) + 7;
341 while (*archSubString
== ' ')
343 return archSubString
;
347 dyld3::Platform
DyldSharedCache::platform() const
349 return (dyld3::Platform
)header
.platform
;
352 #if BUILDING_CACHE_BUILDER
353 std::string
DyldSharedCache::mapFile() const
355 __block
std::string result
;
356 __block
std::vector
<uint64_t> regionStartAddresses
;
357 __block
std::vector
<uint64_t> regionSizes
;
358 __block
std::vector
<uint64_t> regionFileOffsets
;
360 result
.reserve(256*1024);
361 forEachRegion(^(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
362 regionStartAddresses
.push_back(vmAddr
);
363 regionSizes
.push_back(size
);
364 regionFileOffsets
.push_back((uint8_t*)content
- (uint8_t*)this);
365 char lineBuffer
[256];
366 const char* prot
= "RW";
367 if ( permissions
== (VM_PROT_EXECUTE
|VM_PROT_READ
) )
369 else if ( permissions
== VM_PROT_READ
)
371 if ( size
> 1024*1024 )
372 sprintf(lineBuffer
, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot
, size
/(1024*1024), vmAddr
, vmAddr
+size
);
374 sprintf(lineBuffer
, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot
, size
/1024, vmAddr
, vmAddr
+size
);
375 result
+= lineBuffer
;
378 // TODO: add linkedit breakdown
381 forEachImage(^(const mach_header
* mh
, const char* installName
) {
382 result
+= std::string(installName
) + "\n";
383 const dyld3::MachOFile
* mf
= (dyld3::MachOFile
*)mh
;
384 mf
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& info
, bool& stop
) {
385 char lineBuffer
[256];
386 sprintf(lineBuffer
, "\t%16s 0x%08llX -> 0x%08llX\n", info
.segName
, info
.vmAddr
, info
.vmAddr
+info
.vmSize
);
387 result
+= lineBuffer
;
397 uint64_t DyldSharedCache::unslidLoadAddress() const
399 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
400 return mappings
[0].address
;
403 void DyldSharedCache::getUUID(uuid_t uuid
) const
405 memcpy(uuid
, header
.uuid
, sizeof(uuid_t
));
408 uint64_t DyldSharedCache::mappedSize() const
410 __block
uint64_t startAddr
= 0;
411 __block
uint64_t endAddr
= 0;
412 forEachRegion(^(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
413 if ( startAddr
== 0 )
415 uint64_t end
= vmAddr
+size
;
419 return (endAddr
- startAddr
);
422 bool DyldSharedCache::findMachHeaderImageIndex(const mach_header
* mh
, uint32_t& imageIndex
) const
424 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
425 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
426 uint64_t unslidMh
= (uintptr_t)mh
- slide
;
427 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
428 for (uint32_t i
=0; i
< header
.imagesCount
; ++i
) {
429 if ( dylibs
[i
].address
== unslidMh
) {
437 bool DyldSharedCache::hasImagePath(const char* dylibPath
, uint32_t& imageIndex
) const
439 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
440 if ( mappings
[0].fileOffset
!= 0 )
442 if ( header
.mappingOffset
>= 0x118 ) {
443 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
444 const uint8_t* dylibTrieStart
= (uint8_t*)(this->header
.dylibsTrieAddr
+ slide
);
445 const uint8_t* dylibTrieEnd
= dylibTrieStart
+ this->header
.dylibsTrieSize
;
448 const uint8_t* imageNode
= dyld3::MachOLoaded::trieWalk(diag
, dylibTrieStart
, dylibTrieEnd
, dylibPath
);
449 if ( imageNode
!= NULL
) {
450 imageIndex
= (uint32_t)dyld3::MachOFile::read_uleb128(diag
, imageNode
, dylibTrieEnd
);
455 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
456 uint64_t firstImageOffset
= 0;
457 uint64_t firstRegionAddress
= mappings
[0].address
;
458 for (uint32_t i
=0; i
< header
.imagesCount
; ++i
) {
459 const char* aPath
= (char*)this + dylibs
[i
].pathFileOffset
;
460 if ( strcmp(aPath
, dylibPath
) == 0 ) {
464 uint64_t offset
= dylibs
[i
].address
- firstRegionAddress
;
465 if ( firstImageOffset
== 0 )
466 firstImageOffset
= offset
;
468 if ( dylibs
[i
].pathFileOffset
< firstImageOffset
)
476 bool DyldSharedCache::hasNonOverridablePath(const char* dylibPath
) const
478 // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib
479 bool pathIsInDyldCacheWhichCannotBeOverridden
= false;
480 if ( header
.cacheType
== kDyldSharedCacheTypeProduction
) {
482 pathIsInDyldCacheWhichCannotBeOverridden
= this->hasImagePath(dylibPath
, imageIndex
);
483 if ( pathIsInDyldCacheWhichCannotBeOverridden
&& (strcmp(dylibPath
, "/usr/lib/system/libdispatch.dylib") == 0) )
484 pathIsInDyldCacheWhichCannotBeOverridden
= false;
486 return pathIsInDyldCacheWhichCannotBeOverridden
;
489 const dyld3::closure::Image
* DyldSharedCache::findDlopenOtherImage(const char* path
) const
491 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
492 if ( mappings
[0].fileOffset
!= 0 )
494 if ( header
.mappingOffset
< sizeof(dyld_cache_header
) )
496 if ( header
.otherImageArrayAddr
== 0 )
498 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
499 const uint8_t* dylibTrieStart
= (uint8_t*)(this->header
.otherTrieAddr
+ slide
);
500 const uint8_t* dylibTrieEnd
= dylibTrieStart
+ this->header
.otherTrieSize
;
503 const uint8_t* imageNode
= dyld3::MachOLoaded::trieWalk(diag
, dylibTrieStart
, dylibTrieEnd
, path
);
504 if ( imageNode
!= NULL
) {
505 dyld3::closure::ImageNum imageNum
= (uint32_t)dyld3::MachOFile::read_uleb128(diag
, imageNode
, dylibTrieEnd
);
506 uint64_t arrayAddrOffset
= header
.otherImageArrayAddr
- mappings
[0].address
;
507 const dyld3::closure::ImageArray
* otherImageArray
= (dyld3::closure::ImageArray
*)((char*)this + arrayAddrOffset
);
508 return otherImageArray
->imageForNum(imageNum
);
517 const dyld3::closure::LaunchClosure
* DyldSharedCache::findClosure(const char* executablePath
) const
519 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
520 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
521 const uint8_t* executableTrieStart
= (uint8_t*)(this->header
.progClosuresTrieAddr
+ slide
);
522 const uint8_t* executableTrieEnd
= executableTrieStart
+ this->header
.progClosuresTrieSize
;
523 const uint8_t* closuresStart
= (uint8_t*)(this->header
.progClosuresAddr
+ slide
);
526 const uint8_t* imageNode
= dyld3::MachOLoaded::trieWalk(diag
, executableTrieStart
, executableTrieEnd
, executablePath
);
527 if ( (imageNode
== NULL
) && (strncmp(executablePath
, "/System/", 8) == 0) ) {
528 // anything in /System/ should have a closure. Perhaps it was launched via symlink path
529 char realPath
[PATH_MAX
];
530 if ( realpath(executablePath
, realPath
) != NULL
)
531 imageNode
= dyld3::MachOLoaded::trieWalk(diag
, executableTrieStart
, executableTrieEnd
, realPath
);
533 if ( imageNode
!= NULL
) {
534 uint32_t closureOffset
= (uint32_t)dyld3::MachOFile::read_uleb128(diag
, imageNode
, executableTrieEnd
);
535 if ( closureOffset
< this->header
.progClosuresSize
)
536 return (dyld3::closure::LaunchClosure
*)((uint8_t*)closuresStart
+ closureOffset
);
542 #if !BUILDING_LIBDYLD && !BUILDING_DYLD
543 void DyldSharedCache::forEachLaunchClosure(void (^handler
)(const char* executableRuntimePath
, const dyld3::closure::LaunchClosure
* closure
)) const
545 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
546 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
547 const uint8_t* executableTrieStart
= (uint8_t*)(this->header
.progClosuresTrieAddr
+ slide
);
548 const uint8_t* executableTrieEnd
= executableTrieStart
+ this->header
.progClosuresTrieSize
;
549 const uint8_t* closuresStart
= (uint8_t*)(this->header
.progClosuresAddr
+ slide
);
551 std::vector
<DylibIndexTrie::Entry
> closureEntries
;
552 if ( Trie
<DylibIndex
>::parseTrie(executableTrieStart
, executableTrieEnd
, closureEntries
) ) {
553 for (DylibIndexTrie::Entry
& entry
: closureEntries
) {
554 uint32_t offset
= entry
.info
.index
;
555 if ( offset
< this->header
.progClosuresSize
)
556 handler(entry
.name
.c_str(), (const dyld3::closure::LaunchClosure
*)(closuresStart
+offset
));
561 void DyldSharedCache::forEachDlopenImage(void (^handler
)(const char* runtimePath
, const dyld3::closure::Image
* image
)) const
563 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
564 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
565 const uint8_t* otherTrieStart
= (uint8_t*)(this->header
.otherTrieAddr
+ slide
);
566 const uint8_t* otherTrieEnd
= otherTrieStart
+ this->header
.otherTrieSize
;
568 std::vector
<DylibIndexTrie::Entry
> otherEntries
;
569 if ( Trie
<DylibIndex
>::parseTrie(otherTrieStart
, otherTrieEnd
, otherEntries
) ) {
570 for (const DylibIndexTrie::Entry
& entry
: otherEntries
) {
571 dyld3::closure::ImageNum imageNum
= entry
.info
.index
;
572 uint64_t arrayAddrOffset
= header
.otherImageArrayAddr
- mappings
[0].address
;
573 const dyld3::closure::ImageArray
* otherImageArray
= (dyld3::closure::ImageArray
*)((char*)this + arrayAddrOffset
);
574 handler(entry
.name
.c_str(), otherImageArray
->imageForNum(imageNum
));
580 const dyld3::closure::ImageArray
* DyldSharedCache::cachedDylibsImageArray() const
582 // check for old cache without imagesArray
583 if ( header
.mappingOffset
< 0x100 )
586 if ( header
.dylibsImageArrayAddr
== 0 )
589 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
590 uint64_t arrayAddrOffset
= header
.dylibsImageArrayAddr
- mappings
[0].address
;
591 return (dyld3::closure::ImageArray
*)((char*)this + arrayAddrOffset
);
594 const dyld3::closure::ImageArray
* DyldSharedCache::otherOSImageArray() const
596 // check for old cache without imagesArray
597 if ( header
.mappingOffset
< sizeof(dyld_cache_header
) )
600 if ( header
.otherImageArrayAddr
== 0 )
603 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
604 uint64_t arrayAddrOffset
= header
.otherImageArrayAddr
- mappings
[0].address
;
605 return (dyld3::closure::ImageArray
*)((char*)this + arrayAddrOffset
);
609 uint32_t DyldSharedCache::patchableExportCount(uint32_t imageIndex
) const {
610 if ( header
.patchInfoAddr
== 0 )
613 const dyld_cache_patch_info
* patchInfo
= getAddrField
<dyld_cache_patch_info
*>(header
.patchInfoAddr
);
614 const dyld_cache_image_patches
* patchArray
= getAddrField
<dyld_cache_image_patches
*>(patchInfo
->patchTableArrayAddr
);
615 if (imageIndex
> patchInfo
->patchTableArrayCount
)
617 return patchArray
[imageIndex
].patchExportsCount
;
620 void DyldSharedCache::forEachPatchableExport(uint32_t imageIndex
, void (^handler
)(uint32_t cacheOffsetOfImpl
, const char* exportName
)) const {
621 if ( header
.patchInfoAddr
== 0 )
624 const dyld_cache_patch_info
* patchInfo
= getAddrField
<dyld_cache_patch_info
*>(header
.patchInfoAddr
);
625 const dyld_cache_image_patches
* patchArray
= getAddrField
<dyld_cache_image_patches
*>(patchInfo
->patchTableArrayAddr
);
626 if (imageIndex
> patchInfo
->patchTableArrayCount
)
628 const dyld_cache_image_patches
& patch
= patchArray
[imageIndex
];
629 if ( (patch
.patchExportsStartIndex
+ patch
.patchExportsCount
) > patchInfo
->patchExportArrayCount
)
631 const dyld_cache_patchable_export
* patchExports
= getAddrField
<dyld_cache_patchable_export
*>(patchInfo
->patchExportArrayAddr
);
632 const char* exportNames
= getAddrField
<char*>(patchInfo
->patchExportNamesAddr
);
633 for (uint64_t exportIndex
= 0; exportIndex
!= patch
.patchExportsCount
; ++exportIndex
) {
634 const dyld_cache_patchable_export
& patchExport
= patchExports
[patch
.patchExportsStartIndex
+ exportIndex
];
635 const char* exportName
= ( patchExport
.exportNameOffset
< patchInfo
->patchExportNamesSize
) ? &exportNames
[patchExport
.exportNameOffset
] : "";
636 handler(patchExport
.cacheOffsetOfImpl
, exportName
);
640 void DyldSharedCache::forEachPatchableUseOfExport(uint32_t imageIndex
, uint32_t cacheOffsetOfImpl
,
641 void (^handler
)(dyld_cache_patchable_location patchLocation
)) const {
642 if ( header
.patchInfoAddr
== 0 )
645 // Loading a new cache so get the data from the cache header
646 const dyld_cache_patch_info
* patchInfo
= getAddrField
<dyld_cache_patch_info
*>(header
.patchInfoAddr
);
647 const dyld_cache_image_patches
* patchArray
= getAddrField
<dyld_cache_image_patches
*>(patchInfo
->patchTableArrayAddr
);
648 if (imageIndex
> patchInfo
->patchTableArrayCount
)
650 const dyld_cache_image_patches
& patch
= patchArray
[imageIndex
];
651 if ( (patch
.patchExportsStartIndex
+ patch
.patchExportsCount
) > patchInfo
->patchExportArrayCount
)
653 const dyld_cache_patchable_export
* patchExports
= getAddrField
<dyld_cache_patchable_export
*>(patchInfo
->patchExportArrayAddr
);
654 const dyld_cache_patchable_location
* patchLocations
= getAddrField
<dyld_cache_patchable_location
*>(patchInfo
->patchLocationArrayAddr
);
655 for (uint64_t exportIndex
= 0; exportIndex
!= patch
.patchExportsCount
; ++exportIndex
) {
656 const dyld_cache_patchable_export
& patchExport
= patchExports
[patch
.patchExportsStartIndex
+ exportIndex
];
657 if ( patchExport
.cacheOffsetOfImpl
!= cacheOffsetOfImpl
)
659 if ( (patchExport
.patchLocationsStartIndex
+ patchExport
.patchLocationsCount
) > patchInfo
->patchLocationArrayCount
)
661 for (uint64_t locationIndex
= 0; locationIndex
!= patchExport
.patchLocationsCount
; ++locationIndex
) {
662 const dyld_cache_patchable_location
& patchLocation
= patchLocations
[patchExport
.patchLocationsStartIndex
+ locationIndex
];
663 handler(patchLocation
);
668 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
669 // MRM map file generator
670 std::string
DyldSharedCache::generateJSONMap(const char* disposition
) const {
671 dyld3::json::Node cacheNode
;
673 cacheNode
.map
["version"].value
= "1";
674 cacheNode
.map
["disposition"].value
= disposition
;
675 cacheNode
.map
["base-address"].value
= dyld3::json::hex(unslidLoadAddress());
678 uuid_string_t cache_uuidStr
;
679 uuid_unparse(cache_uuid
, cache_uuidStr
);
680 cacheNode
.map
["uuid"].value
= cache_uuidStr
;
682 __block
dyld3::json::Node imagesNode
;
683 forEachImage(^(const mach_header
*mh
, const char *installName
) {
684 dyld3::json::Node imageNode
;
685 imageNode
.map
["path"].value
= installName
;
686 dyld3::MachOAnalyzer
* ma
= (dyld3::MachOAnalyzer
*)mh
;
688 if (ma
->getUuid(uuid
)) {
689 uuid_string_t uuidStr
;
690 uuid_unparse(uuid
, uuidStr
);
691 imageNode
.map
["uuid"].value
= uuidStr
;
694 __block
dyld3::json::Node segmentsNode
;
695 ma
->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo
&info
, bool &stop
) {
696 dyld3::json::Node segmentNode
;
697 segmentNode
.map
["name"].value
= info
.segName
;
698 segmentNode
.map
["start-vmaddr"].value
= dyld3::json::hex(info
.vmAddr
);
699 segmentNode
.map
["end-vmaddr"].value
= dyld3::json::hex(info
.vmAddr
+ info
.vmSize
);
700 segmentsNode
.array
.push_back(segmentNode
);
702 imageNode
.map
["segments"] = segmentsNode
;
703 imagesNode
.array
.push_back(imageNode
);
706 cacheNode
.map
["images"] = imagesNode
;
708 std::stringstream stream
;
709 printJSON(cacheNode
, 0, stream
);
714 std::string
DyldSharedCache::generateJSONDependents() const {
715 std::unordered_map
<std::string
, std::set
<std::string
>> dependents
;
716 computeTransitiveDependents(dependents
);
718 std::stringstream stream
;
722 for (auto p
: dependents
) {
723 if (!first
) stream
<< "," << std::endl
;
726 stream
<< "\"" << p
.first
<< "\" : [" << std::endl
;
727 bool firstDependent
= true;
728 for (const std::string
& dependent
: p
.second
) {
729 if (!firstDependent
) stream
<< "," << std::endl
;
730 firstDependent
= false;
731 stream
<< " \"" << dependent
<< "\"";
733 stream
<< "]" << std::endl
;
735 stream
<< "}" << std::endl
;
743 const dyld_cache_slide_info
* DyldSharedCache::slideInfo() const
745 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
746 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
748 uint64_t offsetInLinkEditRegion
= (header
.slideInfoOffset
- mappings
[2].fileOffset
);
749 return (dyld_cache_slide_info
*)((uint8_t*)(mappings
[2].address
) + slide
+ offsetInLinkEditRegion
);
752 const uint8_t* DyldSharedCache::dataRegionStart() const
754 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
755 uintptr_t slide
= (uintptr_t)this - (uintptr_t)(mappings
[0].address
);
757 return (uint8_t*)(mappings
[1].address
) + slide
;
760 const objc_opt::objc_opt_t
* DyldSharedCache::objcOpt() const {
761 // Find the objc image
762 const dyld3::MachOAnalyzer
* objcMA
= nullptr;
765 if ( hasImagePath("/usr/lib/libobjc.A.dylib", imageIndex
) ) {
766 const dyld3::closure::ImageArray
* images
= cachedDylibsImageArray();
767 const dyld3::closure::Image
* image
= images
->imageForNum(imageIndex
+1);
768 objcMA
= (const dyld3::MachOAnalyzer
*)((uintptr_t)this + image
->cacheOffset());
773 // If we found the objc image, then try to find the read-only data inside.
774 __block
const uint8_t* objcROContent
= nullptr;
775 int64_t slide
= objcMA
->getSlide();
776 objcMA
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
777 if (strcmp(info
.segInfo
.segName
, "__TEXT") != 0)
779 if (strcmp(info
.sectName
, "__objc_opt_ro") != 0)
781 if ( malformedSectionRange
) {
785 objcROContent
= (uint8_t*)(info
.sectAddr
+ slide
);
788 if (objcROContent
== nullptr)
791 const objc_opt::objc_opt_t
* optObjCHeader
= (const objc_opt::objc_opt_t
*)objcROContent
;
792 return optObjCHeader
->version
== objc_opt::VERSION
? optObjCHeader
: nullptr;
795 const void* DyldSharedCache::objcOptPtrs() const {
796 // Find the objc image
797 const dyld3::MachOAnalyzer
* objcMA
= nullptr;
800 if ( hasImagePath("/usr/lib/libobjc.A.dylib", imageIndex
) ) {
801 const dyld3::closure::ImageArray
* images
= cachedDylibsImageArray();
802 const dyld3::closure::Image
* image
= images
->imageForNum(imageIndex
+1);
803 objcMA
= (const dyld3::MachOAnalyzer
*)((uintptr_t)this + image
->cacheOffset());
808 // If we found the objc image, then try to find the read-only data inside.
809 __block
const void* objcPointersContent
= nullptr;
810 int64_t slide
= objcMA
->getSlide();
811 uint32_t pointerSize
= objcMA
->pointerSize();
812 objcMA
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
& info
, bool malformedSectionRange
, bool& stop
) {
813 if ( strncmp(info
.segInfo
.segName
, "__DATA", 6) != 0 )
815 if (strcmp(info
.sectName
, "__objc_opt_ptrs") != 0)
817 if ( info
.sectSize
!= pointerSize
) {
821 if ( malformedSectionRange
) {
825 objcPointersContent
= (uint8_t*)(info
.sectAddr
+ slide
);
828 return objcPointersContent
;
831 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
832 void DyldSharedCache::fillMachOAnalyzersMap(std::unordered_map
<std::string
,dyld3::MachOAnalyzer
*> & dylibAnalyzers
) const {
833 forEachImage(^(const mach_header
*mh
, const char *iteratedInstallName
) {
834 dylibAnalyzers
[std::string(iteratedInstallName
)] = (dyld3::MachOAnalyzer
*)mh
;
838 void DyldSharedCache::computeReverseDependencyMapForDylib(std::unordered_map
<std::string
, std::set
<std::string
>> &reverseDependencyMap
, const std::unordered_map
<std::string
,dyld3::MachOAnalyzer
*> & dylibAnalyzers
, const std::string
&loadPath
) const {
839 dyld3::MachOAnalyzer
*ma
= dylibAnalyzers
.at(loadPath
);
840 if (reverseDependencyMap
.find(loadPath
) != reverseDependencyMap
.end()) return;
841 reverseDependencyMap
[loadPath
] = std::set
<std::string
>();
843 ma
->forEachDependentDylib(^(const char *dependencyLoadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
844 if (isUpward
) return;
845 std::string dependencyLoadPathString
= std::string(dependencyLoadPath
);
846 computeReverseDependencyMapForDylib(reverseDependencyMap
, dylibAnalyzers
, dependencyLoadPathString
);
847 reverseDependencyMap
[dependencyLoadPathString
].insert(loadPath
);
851 // Walks the shared cache and construct the reverse dependency graph (if dylib A depends on B,
852 // constructs the graph with B -> A edges)
853 void DyldSharedCache::computeReverseDependencyMap(std::unordered_map
<std::string
, std::set
<std::string
>> &reverseDependencyMap
) const {
854 std::unordered_map
<std::string
,dyld3::MachOAnalyzer
*> dylibAnalyzers
;
856 fillMachOAnalyzersMap(dylibAnalyzers
);
857 forEachImage(^(const mach_header
*mh
, const char *installName
) {
858 computeReverseDependencyMapForDylib(reverseDependencyMap
, dylibAnalyzers
, std::string(installName
));
862 // uses the reverse dependency graph constructed above to find the recursive set of dependents for each dylib
863 void DyldSharedCache::findDependentsRecursively(std::unordered_map
<std::string
, std::set
<std::string
>> &transitiveDependents
, const std::unordered_map
<std::string
, std::set
<std::string
>> &reverseDependencyMap
, std::set
<std::string
> & visited
, const std::string
&loadPath
) const {
865 if (transitiveDependents
.find(loadPath
) != transitiveDependents
.end()) {
869 if (visited
.find(loadPath
) != visited
.end()) {
873 visited
.insert(loadPath
);
875 std::set
<std::string
> dependents
;
877 for (const std::string
& dependent
: reverseDependencyMap
.at(loadPath
)) {
878 findDependentsRecursively(transitiveDependents
, reverseDependencyMap
, visited
, dependent
);
879 if (transitiveDependents
.find(dependent
) != transitiveDependents
.end()) {
880 std::set
<std::string
> & theseTransitiveDependents
= transitiveDependents
.at(dependent
);
881 dependents
.insert(theseTransitiveDependents
.begin(), theseTransitiveDependents
.end());
883 dependents
.insert(dependent
);
886 transitiveDependents
[loadPath
] = dependents
;
889 // Fills a map from each install name N to the set of install names depending on N
890 void DyldSharedCache::computeTransitiveDependents(std::unordered_map
<std::string
, std::set
<std::string
>> & transitiveDependents
) const {
891 std::unordered_map
<std::string
, std::set
<std::string
>> reverseDependencyMap
;
892 computeReverseDependencyMap(reverseDependencyMap
);
893 forEachImage(^(const mach_header
*mh
, const char *installName
) {
894 std::set
<std::string
> visited
;
895 findDependentsRecursively(transitiveDependents
, reverseDependencyMap
, visited
, std::string(installName
));