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 results
.overflowed
= 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 if ( cache
.errorMessage().empty() ) {
74 results
.cacheContent
= cache
.buffer();
75 results
.cacheLength
= cache
.bufferSize();
79 results
.cacheContent
= nullptr;
80 results
.cacheLength
= 0;
81 results
.errorMessage
= cache
.errorMessage();
86 bool DyldSharedCache::verifySelfContained(std::vector
<MappedMachO
>& dylibsToCache
, MappedMachO (^loader
)(const std::string
& runtimePath
), std::vector
<std::pair
<DyldSharedCache::MappedMachO
, std::set
<std::string
>>>& rejected
)
89 // build map of dylibs
90 __block
std::map
<std::string
, std::set
<std::string
>> badDylibs
;
91 __block
std::set
<std::string
> knownDylibs
;
92 for (const DyldSharedCache::MappedMachO
& dylib
: dylibsToCache
) {
93 std::set
<std::string
> reasons
;
94 dyld3::MachOParser
parser(dylib
.mh
);
95 if (parser
.canBePlacedInDyldCache(dylib
.runtimePath
, reasons
)) {
96 knownDylibs
.insert(dylib
.runtimePath
);
97 knownDylibs
.insert(parser
.installName());
99 badDylibs
[dylib
.runtimePath
] = reasons
;
103 // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
104 __block
bool doAgain
= true;
106 __block
std::vector
<DyldSharedCache::MappedMachO
> foundMappings
;
108 // scan dylib list making sure all dependents are in dylib list
109 for (const DyldSharedCache::MappedMachO
& dylib
: dylibsToCache
) {
110 if ( badDylibs
.count(dylib
.runtimePath
) != 0 )
112 dyld3::MachOParser
parser(dylib
.mh
);
113 parser
.forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
114 if ( knownDylibs
.count(loadPath
) == 0 ) {
116 MappedMachO foundMapping
;
117 if ( badDylibs
.count(loadPath
) == 0 )
118 foundMapping
= loader(loadPath
);
119 if ( foundMapping
.length
== 0 ) {
120 std::string reason
= std::string("Could not find dependency '") + loadPath
+"'";
121 auto i
= badDylibs
.find(dylib
.runtimePath
);
122 if (i
== badDylibs
.end()) {
123 std::set
<std::string
> reasons
;
124 reasons
.insert(reason
);
125 badDylibs
[dylib
.runtimePath
] = reasons
;
127 i
->second
.insert(reason
);
129 knownDylibs
.erase(dylib
.runtimePath
);
130 dyld3::MachOParser
parserBad(dylib
.mh
);
131 knownDylibs
.erase(parserBad
.installName());
134 dyld3::MachOParser
foundParser(foundMapping
.mh
);
135 std::set
<std::string
> reasons
;
136 if (foundParser
.canBePlacedInDyldCache(foundParser
.installName(), reasons
)) {
137 foundMappings
.push_back(foundMapping
);
138 knownDylibs
.insert(foundMapping
.runtimePath
);
139 knownDylibs
.insert(foundParser
.installName());
141 auto i
= badDylibs
.find(dylib
.runtimePath
);
142 if (i
== badDylibs
.end()) {
143 badDylibs
[dylib
.runtimePath
] = reasons
;
145 i
->second
.insert(reasons
.begin(), reasons
.end());
152 dylibsToCache
.insert(dylibsToCache
.end(), foundMappings
.begin(), foundMappings
.end());
154 const auto badDylibsCopy
= badDylibs
;
155 dylibsToCache
.erase(std::remove_if(dylibsToCache
.begin(), dylibsToCache
.end(), [&](const DyldSharedCache::MappedMachO
& dylib
) {
156 auto i
= badDylibsCopy
.find(dylib
.runtimePath
);
157 if ( i
!= badDylibsCopy
.end()) {
158 rejected
.push_back(std::make_pair(dylib
, i
->second
));
164 }), dylibsToCache
.end());
167 return badDylibs
.empty();
171 void DyldSharedCache::forEachRegion(void (^handler
)(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
)) const
173 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
174 const dyld_cache_mapping_info
* mappingsEnd
= &mappings
[header
.mappingCount
];
175 for (const dyld_cache_mapping_info
* m
=mappings
; m
< mappingsEnd
; ++m
) {
176 handler((char*)this + m
->fileOffset
, m
->address
, m
->size
, m
->initProt
);
180 void DyldSharedCache::forEachImage(void (^handler
)(const mach_header
* mh
, const char* installName
)) const
182 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
183 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
184 if ( mappings
[0].fileOffset
!= 0 )
186 uint64_t firstImageOffset
= 0;
187 uint64_t firstRegionAddress
= mappings
[0].address
;
188 for (uint32_t i
=0; i
< header
.imagesCount
; ++i
) {
189 const char* dylibPath
= (char*)this + dylibs
[i
].pathFileOffset
;
190 uint64_t offset
= dylibs
[i
].address
- firstRegionAddress
;
191 if ( firstImageOffset
== 0 )
192 firstImageOffset
= offset
;
194 if ( dylibs
[i
].pathFileOffset
< firstImageOffset
)
196 const mach_header
* mh
= (mach_header
*)((char*)this + offset
);
197 handler(mh
, dylibPath
);
201 void DyldSharedCache::forEachImageEntry(void (^handler
)(const char* path
, uint64_t mTime
, uint64_t inode
)) const
203 const dyld_cache_image_info
* dylibs
= (dyld_cache_image_info
*)((char*)this + header
.imagesOffset
);
204 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
205 if ( mappings
[0].fileOffset
!= 0 )
207 uint64_t firstImageOffset
= 0;
208 uint64_t firstRegionAddress
= mappings
[0].address
;
209 for (uint32_t i
=0; i
< header
.imagesCount
; ++i
) {
210 const char* dylibPath
= (char*)this + dylibs
[i
].pathFileOffset
;
211 uint64_t offset
= dylibs
[i
].address
- firstRegionAddress
;
212 if ( firstImageOffset
== 0 )
213 firstImageOffset
= offset
;
215 if ( dylibs
[i
].pathFileOffset
< firstImageOffset
)
217 handler(dylibPath
, dylibs
[i
].modTime
, dylibs
[i
].inode
);
221 void DyldSharedCache::forEachImageTextSegment(void (^handler
)(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const uuid_t dylibUUID
, const char* installName
)) const
223 // check for old cache without imagesText array
224 if ( header
.mappingOffset
< 123 )
227 // walk imageText table and call callback for each entry
228 const dyld_cache_image_text_info
* imagesText
= (dyld_cache_image_text_info
*)((char*)this + header
.imagesTextOffset
);
229 const dyld_cache_image_text_info
* imagesTextEnd
= &imagesText
[header
.imagesTextCount
];
230 for (const dyld_cache_image_text_info
* p
=imagesText
; p
< imagesTextEnd
; ++p
) {
231 handler(p
->loadAddress
, p
->textSegmentSize
, p
->uuid
, (char*)this + p
->pathOffset
);
236 std::string
DyldSharedCache::archName() const
238 const char* archSubString
= ((char*)this) + 8;
239 while (*archSubString
== ' ')
241 return archSubString
;
245 uint32_t DyldSharedCache::platform() const
247 return header
.platform
;
251 std::string
DyldSharedCache::mapFile() const
253 __block
std::string result
;
254 __block
std::vector
<uint64_t> regionStartAddresses
;
255 __block
std::vector
<uint64_t> regionSizes
;
256 __block
std::vector
<uint64_t> regionFileOffsets
;
258 result
.reserve(256*1024);
259 forEachRegion(^(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
260 regionStartAddresses
.push_back(vmAddr
);
261 regionSizes
.push_back(size
);
262 regionFileOffsets
.push_back((uint8_t*)content
- (uint8_t*)this);
263 char lineBuffer
[256];
264 const char* prot
= "RW";
265 if ( permissions
== (VM_PROT_EXECUTE
|VM_PROT_READ
) )
267 else if ( permissions
== VM_PROT_READ
)
269 if ( size
> 1024*1024 )
270 sprintf(lineBuffer
, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot
, size
/(1024*1024), vmAddr
, vmAddr
+size
);
272 sprintf(lineBuffer
, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot
, size
/1024, vmAddr
, vmAddr
+size
);
273 result
+= lineBuffer
;
276 // TODO: add linkedit breakdown
279 forEachImage(^(const mach_header
* mh
, const char* installName
) {
280 result
+= std::string(installName
) + "\n";
281 dyld3::MachOParser
parser(mh
);
282 parser
.forEachSegment(^(const char* segName
, uint32_t fileOffset
, uint32_t fileSize
, uint64_t vmAddr
, uint64_t vmSize
, uint8_t protections
, bool& stop
) {
283 char lineBuffer
[256];
284 sprintf(lineBuffer
, "\t%16s 0x%08llX -> 0x%08llX\n", segName
, vmAddr
, vmAddr
+vmSize
);
285 result
+= lineBuffer
;
295 uint64_t DyldSharedCache::unslidLoadAddress() const
297 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)((char*)this + header
.mappingOffset
);
298 return mappings
[0].address
;
301 void DyldSharedCache::getUUID(uuid_t uuid
) const
303 memcpy(uuid
, header
.uuid
, sizeof(uuid_t
));
306 uint64_t DyldSharedCache::mappedSize() const
308 __block
uint64_t startAddr
= 0;
309 __block
uint64_t endAddr
= 0;
310 forEachRegion(^(const void* content
, uint64_t vmAddr
, uint64_t size
, uint32_t permissions
) {
311 if ( startAddr
== 0 )
313 uint64_t end
= vmAddr
+size
;
317 return (endAddr
- startAddr
);