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>
37 #include <CommonCrypto/CommonDigest.h>
38 #include <CommonCrypto/CommonDigestSPI.h>
44 #include <unordered_map>
45 #include <unordered_set>
49 #include "MachOParser.h"
50 #include "CacheBuilder.h"
51 #include "DyldSharedCache.h"
52 #include "LaunchCache.h"
54 #include "StringUtils.h"
59 DyldSharedCache::CreateResults
DyldSharedCache::create(const CreateOptions
& options
,
60 const std::vector
<MappedMachO
>& dylibsToCache
,
61 const std::vector
<MappedMachO
>& otherOsDylibs
,
62 const std::vector
<MappedMachO
>& osExecutables
)
64 CreateResults results
;
65 CacheBuilder
cache(options
);
67 cache
.build(dylibsToCache
, otherOsDylibs
, osExecutables
);
69 results
.agileSignature
= cache
.agileSignature();
70 results
.cdHashFirst
= cache
.cdHashFirst();
71 results
.cdHashSecond
= cache
.cdHashSecond();
72 results
.warnings
= cache
.warnings();
73 results
.evictions
= cache
.evictions();
75 if ( cache
.errorMessage().empty() ) {
76 results
.cacheContent
= cache
.buffer();
77 results
.cacheLength
= cache
.bufferSize();
81 results
.cacheContent
= nullptr;
82 results
.cacheLength
= 0;
83 results
.errorMessage
= cache
.errorMessage();
88 bool DyldSharedCache::verifySelfContained(std::vector
<MappedMachO
>& dylibsToCache
, MappedMachO (^loader
)(const std::string
& runtimePath
), std::vector
<std::pair
<DyldSharedCache::MappedMachO
, std::set
<std::string
>>>& rejected
)
91 // build map of dylibs
92 __block
std::map
<std::string
, std::set
<std::string
>> badDylibs
;
93 __block
std::set
<std::string
> knownDylibs
;
94 for (const DyldSharedCache::MappedMachO
& dylib
: dylibsToCache
) {
95 std::set
<std::string
> reasons
;
96 dyld3::MachOParser
parser(dylib
.mh
);
97 if (parser
.canBePlacedInDyldCache(dylib
.runtimePath
, reasons
)) {
98 knownDylibs
.insert(dylib
.runtimePath
);
99 knownDylibs
.insert(parser
.installName());
101 badDylibs
[dylib
.runtimePath
] = reasons
;
105 // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
106 __block
bool doAgain
= true;
108 __block
std::vector
<DyldSharedCache::MappedMachO
> foundMappings
;
110 // scan dylib list making sure all dependents are in dylib list
111 for (const DyldSharedCache::MappedMachO
& dylib
: dylibsToCache
) {
112 if ( badDylibs
.count(dylib
.runtimePath
) != 0 )
114 dyld3::MachOParser
parser(dylib
.mh
);
115 parser
.forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
116 if ( knownDylibs
.count(loadPath
) == 0 ) {
118 MappedMachO foundMapping
;
119 if ( badDylibs
.count(loadPath
) == 0 )
120 foundMapping
= loader(loadPath
);
121 if ( foundMapping
.length
== 0 ) {
122 std::string reason
= std::string("Could not find dependency '") + loadPath
+"'";
123 auto i
= badDylibs
.find(dylib
.runtimePath
);
124 if (i
== badDylibs
.end()) {
125 std::set
<std::string
> reasons
;
126 reasons
.insert(reason
);
127 badDylibs
[dylib
.runtimePath
] = reasons
;
129 i
->second
.insert(reason
);
131 knownDylibs
.erase(dylib
.runtimePath
);
132 dyld3::MachOParser
parserBad(dylib
.mh
);
133 knownDylibs
.erase(parserBad
.installName());
136 dyld3::MachOParser
foundParser(foundMapping
.mh
);
137 std::set
<std::string
> reasons
;
138 if (foundParser
.canBePlacedInDyldCache(foundParser
.installName(), reasons
)) {
139 foundMappings
.push_back(foundMapping
);
140 knownDylibs
.insert(foundMapping
.runtimePath
);
141 knownDylibs
.insert(foundParser
.installName());
143 auto i
= badDylibs
.find(dylib
.runtimePath
);
144 if (i
== badDylibs
.end()) {
145 badDylibs
[dylib
.runtimePath
] = reasons
;
147 i
->second
.insert(reasons
.begin(), reasons
.end());
154 dylibsToCache
.insert(dylibsToCache
.end(), foundMappings
.begin(), foundMappings
.end());
156 const auto badDylibsCopy
= badDylibs
;
157 dylibsToCache
.erase(std::remove_if(dylibsToCache
.begin(), dylibsToCache
.end(), [&](const DyldSharedCache::MappedMachO
& dylib
) {
158 auto i
= badDylibsCopy
.find(dylib
.runtimePath
);
159 if ( i
!= badDylibsCopy
.end()) {
160 rejected
.push_back(std::make_pair(dylib
, i
->second
));
166 }), dylibsToCache
.end());
169 return badDylibs
.empty();
173 void DyldSharedCache::forEachRegion(void (^handler
)(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
)) const
175 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
176 const dyld_cache_mapping_info
* mappingsEnd
= &mappings
[header
.mappingCount
];
177 for (const dyld_cache_mapping_info
* m
=mappings
; m
< mappingsEnd
; ++m
) {
178 handler((char*)this + m
->fileOffset
, m
->address
, m
->size
, m
->initProt
);
182 void DyldSharedCache::forEachImage(void (^handler
)(const mach_header
* mh
, const char* installName
)) const
184 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
185 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
186 if ( mappings
[0].fileOffset
!= 0 )
188 uint64_t firstImageOffset
= 0;
189 uint64_t firstRegionAddress
= mappings
[0].address
;
190 for (uint32_t i
=0; i
< header
.imagesCount
; ++i
) {
191 const char* dylibPath
= (char*)this + dylibs
[i
].pathFileOffset
;
192 uint64_t offset
= dylibs
[i
].address
- firstRegionAddress
;
193 if ( firstImageOffset
== 0 )
194 firstImageOffset
= offset
;
196 if ( dylibs
[i
].pathFileOffset
< firstImageOffset
)
198 const mach_header
* mh
= (mach_header
*)((char*)this + offset
);
199 handler(mh
, dylibPath
);
203 void DyldSharedCache::forEachImageEntry(void (^handler
)(const char* path
, uint64_t mTime
, uint64_t inode
)) const
205 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
206 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
207 if ( mappings
[0].fileOffset
!= 0 )
209 uint64_t firstImageOffset
= 0;
210 uint64_t firstRegionAddress
= mappings
[0].address
;
211 for (uint32_t i
=0; i
< header
.imagesCount
; ++i
) {
212 const char* dylibPath
= (char*)this + dylibs
[i
].pathFileOffset
;
213 uint64_t offset
= dylibs
[i
].address
- firstRegionAddress
;
214 if ( firstImageOffset
== 0 )
215 firstImageOffset
= offset
;
217 if ( dylibs
[i
].pathFileOffset
< firstImageOffset
)
219 handler(dylibPath
, dylibs
[i
].modTime
, dylibs
[i
].inode
);
223 void DyldSharedCache::forEachImageTextSegment(void (^handler
)(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const uuid_t dylibUUID
, const char* installName
)) const
225 // check for old cache without imagesText array
226 if ( header
.mappingOffset
< 123 )
229 // walk imageText table and call callback for each entry
230 const dyld_cache_image_text_info
* imagesText
= (dyld_cache_image_text_info
*)((char*)this + header
.imagesTextOffset
);
231 const dyld_cache_image_text_info
* imagesTextEnd
= &imagesText
[header
.imagesTextCount
];
232 for (const dyld_cache_image_text_info
* p
=imagesText
; p
< imagesTextEnd
; ++p
) {
233 handler(p
->loadAddress
, p
->textSegmentSize
, p
->uuid
, (char*)this + p
->pathOffset
);
238 std::string
DyldSharedCache::archName() const
240 const char* archSubString
= ((char*)this) + 8;
241 while (*archSubString
== ' ')
243 return archSubString
;
247 uint32_t DyldSharedCache::platform() const
249 return header
.platform
;
253 std::string
DyldSharedCache::mapFile() const
255 __block
std::string result
;
256 __block
std::vector
<uint64_t> regionStartAddresses
;
257 __block
std::vector
<uint64_t> regionSizes
;
258 __block
std::vector
<uint64_t> regionFileOffsets
;
260 result
.reserve(256*1024);
261 forEachRegion(^(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
262 regionStartAddresses
.push_back(vmAddr
);
263 regionSizes
.push_back(size
);
264 regionFileOffsets
.push_back((uint8_t*)content
- (uint8_t*)this);
265 char lineBuffer
[256];
266 const char* prot
= "RW";
267 if ( permissions
== (VM_PROT_EXECUTE
|VM_PROT_READ
) )
269 else if ( permissions
== VM_PROT_READ
)
271 if ( size
> 1024*1024 )
272 sprintf(lineBuffer
, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot
, size
/(1024*1024), vmAddr
, vmAddr
+size
);
274 sprintf(lineBuffer
, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot
, size
/1024, vmAddr
, vmAddr
+size
);
275 result
+= lineBuffer
;
278 // TODO: add linkedit breakdown
281 forEachImage(^(const mach_header
* mh
, const char* installName
) {
282 result
+= std::string(installName
) + "\n";
283 dyld3::MachOParser
parser(mh
);
284 parser
.forEachSegment(^(const char* segName
, uint32_t fileOffset
, uint32_t fileSize
, uint64_t vmAddr
, uint64_t vmSize
, uint8_t protections
, bool& stop
) {
285 char lineBuffer
[256];
286 sprintf(lineBuffer
, "\t%16s 0x%08llX -> 0x%08llX\n", segName
, vmAddr
, vmAddr
+vmSize
);
287 result
+= lineBuffer
;
297 uint64_t DyldSharedCache::unslidLoadAddress() const
299 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
300 return mappings
[0].address
;
303 void DyldSharedCache::getUUID(uuid_t uuid
) const
305 memcpy(uuid
, header
.uuid
, sizeof(uuid_t
));
308 uint64_t DyldSharedCache::mappedSize() const
310 __block
uint64_t startAddr
= 0;
311 __block
uint64_t endAddr
= 0;
312 forEachRegion(^(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
313 if ( startAddr
== 0 )
315 uint64_t end
= vmAddr
+size
;
319 return (endAddr
- startAddr
);