dyld-732.8.tar.gz
[apple/dyld.git] / dyld3 / shared-cache / update_dyld_sim_shared_cache.cpp
1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * Copyright (c) 2018 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mman.h>
28 #include <mach/mach.h>
29 #include <mach/mach_time.h>
30 #include <mach-o/dyld.h>
31 #include <limits.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <math.h>
36 #include <fcntl.h>
37 #include <dlfcn.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <sys/uio.h>
42 #include <unistd.h>
43 #include <sys/param.h>
44 #include <sys/sysctl.h>
45 #include <sys/resource.h>
46 #include <dirent.h>
47 #include <rootless.h>
48 #include <dscsym.h>
49 #include <dispatch/dispatch.h>
50 #include <pthread/pthread.h>
51 #include <CoreFoundation/CoreFoundation.h>
52
53 #include <algorithm>
54 #include <vector>
55 #include <unordered_set>
56 #include <unordered_set>
57 #include <iostream>
58 #include <fstream>
59
60 #include "FileUtils.h"
61 #include "StringUtils.h"
62 #include "DyldSharedCache.h"
63 #include "MachOFile.h"
64 #include "MachOAnalyzer.h"
65 #include "ClosureFileSystemPhysical.h"
66
67 struct MappedMachOsByCategory
68 {
69 const dyld3::GradedArchs& archs;
70 std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
71 std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
72 std::vector<DyldSharedCache::MappedMachO> mainExecutables;
73 };
74
75 static const char* sAllowedPrefixes[] = {
76 "/usr",
77 "/System",
78 // don't look at main executables until simulator supports dyld3
79 // "/bin",
80 // "/sbin",
81 };
82
83 static const char* sDontUsePrefixes[] = {
84 "/usr/share",
85 "/usr/local",
86 };
87
88 static const char* sMacOSHostLibs[] = {
89 "/usr/lib/system/libsystem_kernel.dylib",
90 "/usr/lib/system/libsystem_platform.dylib",
91 "/usr/lib/system/libsystem_pthread.dylib",
92 };
93
94 static const char* sMacOSBinaries[] = {
95 "/sbin/launchd_sim_trampoline",
96 "/usr/sbin/iokitsimd",
97 "/usr/lib/system/host/liblaunch_sim.dylib",
98 };
99
100 static bool verbose = false;
101
102
103 static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, std::vector<MappedMachOsByCategory>& files, dyld3::Platform platform)
104 {
105 // don't precompute closure info for any debug or profile dylibs
106 if ( endsWith(runtimePath, "_profile.dylib") || endsWith(runtimePath, "_debug.dylib") || endsWith(runtimePath, "_asan.dylib") || endsWith(runtimePath, "_profile") || endsWith(runtimePath, "_debug") )
107 return false;
108 if ( startsWith(runtimePath, "/usr/lib/system/introspection/") )
109 return false;
110
111 bool result = false;
112 for (MappedMachOsByCategory& file : files) {
113 Diagnostics diag;
114 char realerPath[MAXPATHLEN];
115 dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archs, platform, realerPath);
116 const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
117
118 if ( ma != nullptr ) {
119 bool sipProtected = false; // isProtectedBySIP(fd);
120 bool issetuid = false;
121 const uint64_t sliceLen = loadedFileInfo.sliceLen;
122 if ( ma->isDynamicExecutable() ) {
123 #if 0 // dyld3 not enabled for simulator yet, so don't collect main executables
124 issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
125 file.mainExecutables.emplace_back(runtimePath, ma, sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
126 result = true;
127 #endif
128 }
129 else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {}) ) {
130 file.dylibsForCache.emplace_back(runtimePath, ma, sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
131 result = true;
132 }
133 }
134 }
135
136 return result;
137 }
138
139 static void findAllFiles(const dyld3::closure::FileSystem& fileSystem, const std::vector<std::string>& pathPrefixes, std::vector<MappedMachOsByCategory>& files, dyld3::Platform platform)
140 {
141 std::unordered_set<std::string> skipDirs;
142 for (const char* s : sDontUsePrefixes)
143 skipDirs.insert(s);
144
145 __block std::unordered_set<std::string> alreadyUsed;
146 bool multiplePrefixes = (pathPrefixes.size() > 1);
147 for (const std::string& prefix : pathPrefixes) {
148 // get all files from overlay for this search dir
149 for (const char* searchDir : sAllowedPrefixes ) {
150 iterateDirectoryTree(prefix, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) {
151 // ignore files that don't have 'x' bit set (all runnable mach-o files do)
152 const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH);
153 if ( !hasXBit && !endsWith(path, ".dylib") )
154 return;
155
156 // ignore files too small (must have at least a page of TEXT and LINKEDIT)
157 if ( statBuf.st_size < 0x2000 )
158 return;
159
160 // don't add paths already found using previous prefix
161 if ( multiplePrefixes && (alreadyUsed.count(path) != 0) )
162 return;
163
164 // don't add binaries built for the host Mac OS platform
165 for (std::string s : sMacOSBinaries) {
166 if (s.compare(path) == 0)
167 return;
168 }
169 // if the file is mach-o, add to list
170 if ( addIfMachO(fileSystem, path, statBuf, files, platform) ) {
171 if ( multiplePrefixes )
172 alreadyUsed.insert(path);
173 }
174 });
175 }
176 }
177 }
178
179 static void addMacOSHostLibs(std::vector<MappedMachOsByCategory>& allFileSets, dyld3::Platform platform)
180 {
181 dyld3::closure::FileSystemPhysical fileSystem;
182 for (const char* path : sMacOSHostLibs) {
183 struct stat statBuf;
184 if ( stat(path, &statBuf) == 0 ) {
185 addIfMachO(fileSystem, path, statBuf, allFileSets, dyld3::Platform::macOS);
186 }
187 }
188 }
189
190 static void addMacOSBinaries(const dyld3::closure::FileSystem& fileSystem, const std::vector<std::string>& pathPrefixes, std::vector<MappedMachOsByCategory>& files)
191 {
192 for (const std::string& prefix : pathPrefixes) {
193 for (std::string path : sMacOSBinaries) {
194 std::string fullPath = prefix + path;
195 struct stat statBuf;
196 if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
197 addIfMachO(fileSystem, path, statBuf, files, dyld3::Platform::macOS);
198 }
199 }
200 }
201 }
202
203 static bool dontCache(const std::string& simRuntimeRootPath, const std::string& archName,
204 const std::unordered_set<std::string>& pathsWithDuplicateInstallName,
205 const DyldSharedCache::MappedMachO& aFile, bool warn,
206 const std::unordered_set<std::string>& skipDylibs)
207 {
208 if ( skipDylibs.count(aFile.runtimePath) )
209 return true;
210 if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") )
211 return true;
212 if ( startsWith(aFile.runtimePath, "/usr/local/") )
213 return true;
214
215 // anything inside a .app bundle is specific to app, so should not be in shared cache
216 if ( aFile.runtimePath.find(".app/") != std::string::npos )
217 return true;
218
219 if ( archName == "i386" ) {
220 if ( startsWith(aFile.runtimePath, "/System/Library/CoreServices/") )
221 return true;
222 if ( startsWith(aFile.runtimePath, "/System/Library/Extensions/") )
223 return true;
224 }
225
226 if ( aFile.runtimePath.find("//") != std::string::npos ) {
227 if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s use of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
228 return true;
229 }
230
231 const char* installName = aFile.mh->installName();
232 if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) {
233 // <rdar://problem/46431467> if a dylib moves and a symlink is installed into its place, bom iterator will see both and issue a warning
234 struct stat statBuf;
235 bool isSymLink = ( (lstat(aFile.runtimePath.c_str(), &statBuf) == 0) && S_ISLNK(statBuf.st_mode) );
236 if (!isSymLink && warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
237 return true;
238 }
239
240 if ( aFile.runtimePath != installName ) {
241 // see if install name is a symlink to actual path
242 std::string fullInstall = simRuntimeRootPath + installName;
243 char resolvedPath[PATH_MAX];
244 if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) {
245 std::string resolvedSymlink = resolvedPath;
246 if ( !simRuntimeRootPath.empty() ) {
247 resolvedSymlink = resolvedSymlink.substr(simRuntimeRootPath.size());
248 }
249 if ( aFile.runtimePath == resolvedSymlink ) {
250 return false;
251 }
252 }
253 // <rdar://problem/38000411> also if runtime path is a symlink to install name
254 std::string fullRuntime = simRuntimeRootPath + aFile.runtimePath;
255 if ( realpath(fullRuntime.c_str(), resolvedPath) != NULL ) {
256 std::string resolvedSymlink = resolvedPath;
257 if ( !simRuntimeRootPath.empty() ) {
258 resolvedSymlink = resolvedSymlink.substr(simRuntimeRootPath.size());
259 }
260 if ( resolvedSymlink == installName ) {
261 return false;
262 }
263 }
264 if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
265 return true;
266 }
267 return false;
268 }
269
270 static void pruneCachedDylibs(const std::string& volumePrefix, const std::unordered_set<std::string>& skipDylibs, MappedMachOsByCategory& fileSet)
271 {
272 std::unordered_set<std::string> pathsWithDuplicateInstallName;
273
274 std::unordered_map<std::string, std::string> installNameToFirstPath;
275 for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
276 const char* installName = aFile.mh->installName();
277 auto pos = installNameToFirstPath.find(installName);
278 if ( pos == installNameToFirstPath.end() ) {
279 installNameToFirstPath[installName] = aFile.runtimePath;
280 }
281 else {
282 pathsWithDuplicateInstallName.insert(aFile.runtimePath);
283 pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]);
284 }
285 }
286
287 for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
288 if ( dontCache(volumePrefix, fileSet.archs.name(), pathsWithDuplicateInstallName, aFile, true, skipDylibs) ){
289 // <rdar://problem/46423929> don't build dlopen closures for symlinks to something in the dyld cache
290 if ( pathsWithDuplicateInstallName.count(aFile.runtimePath) == 0 )
291 fileSet.otherDylibsAndBundles.push_back(aFile);
292 }
293 }
294 fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(),
295 [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(volumePrefix, fileSet.archs.name(), pathsWithDuplicateInstallName, aFile, false, skipDylibs); }),
296 fileSet.dylibsForCache.end());
297 }
298
299 static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCategory& fileSet)
300 {
301 // other OS dylibs should not contain dylibs that are embedded in some .app bundle
302 fileSet.otherDylibsAndBundles.erase(std::remove_if(fileSet.otherDylibsAndBundles.begin(), fileSet.otherDylibsAndBundles.end(),
303 [&](const DyldSharedCache::MappedMachO& aFile) { return (aFile.runtimePath.find(".app/") != std::string::npos); }),
304 fileSet.otherDylibsAndBundles.end());
305 }
306
307
308 static bool existingCacheUpToDate(const std::string& existingCache, const std::vector<DyldSharedCache::MappedMachO>& currentDylibs)
309 {
310 // if no existing cache, it is not up-to-date
311 int fd = ::open(existingCache.c_str(), O_RDONLY);
312 if ( fd < 0 )
313 return false;
314 struct stat statbuf;
315 if ( ::fstat(fd, &statbuf) == -1 ) {
316 ::close(fd);
317 return false;
318 }
319
320 // build map of found dylibs
321 std::unordered_map<std::string, const DyldSharedCache::MappedMachO*> currentDylibMap;
322 for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
323 //fprintf(stderr, "0x%0llX 0x%0llX %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str());
324 currentDylibMap[aFile.runtimePath] = &aFile;
325 }
326
327 // make sure all dylibs in existing cache have same mtime and inode as found dylib
328 __block bool foundMismatch = false;
329 const uint64_t cacheMapLen = statbuf.st_size;
330 void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0);
331 if ( p != MAP_FAILED ) {
332 const DyldSharedCache* cache = (DyldSharedCache*)p;
333 cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) {
334 bool foundMatch = false;
335 auto pos = currentDylibMap.find(installName);
336 if ( pos != currentDylibMap.end() ) {
337 const DyldSharedCache::MappedMachO* foundDylib = pos->second;
338 if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) {
339 foundMatch = true;
340 }
341 }
342 if ( !foundMatch ) {
343 // use slow path and look for any dylib with a matching inode and mtime
344 bool foundSlow = false;
345 for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
346 if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) {
347 foundSlow = true;
348 break;
349 }
350 }
351 if ( !foundSlow ) {
352 foundMismatch = true;
353 if ( verbose )
354 fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName);
355 }
356 }
357 });
358 ::munmap(p, cacheMapLen);
359 }
360
361 ::close(fd);
362
363 return !foundMismatch;
364 }
365
366
367 inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
368 {
369 return (uint32_t)(abstime/1000/1000);
370 }
371
372
373 #define TERMINATE_IF_LAST_ARG( s ) \
374 do { \
375 if ( i == argc - 1 ) { \
376 fprintf(stderr, s ); \
377 return 1; \
378 } \
379 } while ( 0 )
380
381 int main(int argc, const char* argv[], const char* envp[])
382 {
383 std::string rootPath;
384 bool force = false;
385 bool dylibsRemoved = false;
386 std::string cacheDir;
387 std::string dylibOrderFile;
388 std::string dirtyDataOrderFile;
389 dyld3::Platform platform = dyld3::Platform::iOS_simulator;
390 std::unordered_set<std::string> skipDylibs;
391
392 // parse command line options
393 for (int i = 1; i < argc; ++i) {
394 const char* arg = argv[i];
395 if (strcmp(arg, "-debug") == 0) {
396 verbose = true;
397 }
398 else if (strcmp(arg, "-verbose") == 0) {
399 verbose = true;
400 }
401 else if ((strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0)) {
402 TERMINATE_IF_LAST_ARG("-root missing path argument\n");
403 rootPath = argv[++i];
404 }
405 else if (strcmp(arg, "-cache_dir") == 0) {
406 TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n");
407 cacheDir = argv[++i];
408 }
409 else if (strcmp(arg, "-iOS") == 0) {
410 platform = dyld3::Platform::iOS_simulator;
411 }
412 else if (strcmp(arg, "-watchOS") == 0) {
413 platform = dyld3::Platform::watchOS_simulator;
414 }
415 else if (strcmp(arg, "-tvOS") == 0) {
416 platform = dyld3::Platform::tvOS_simulator;
417 }
418 else if (strcmp(arg, "-dylibs_removed_in_mastering") == 0) {
419 dylibsRemoved = true;
420 }
421 else if (strcmp(arg, "-dylib_order_file") == 0) {
422 TERMINATE_IF_LAST_ARG("-dylib_order_file missing path argument\n");
423 dylibOrderFile = argv[++i];
424 }
425 else if (strcmp(arg, "-dirty_data_order_file") == 0) {
426 TERMINATE_IF_LAST_ARG("-dirty_data_order_file missing path argument\n");
427 dirtyDataOrderFile = argv[++i];
428 }
429 else if (strcmp(arg, "-force") == 0) {
430 force = true;
431 }
432 else if (strcmp(arg, "-skip") == 0) {
433 TERMINATE_IF_LAST_ARG("-skip missing argument\n");
434 skipDylibs.insert(argv[++i]);
435 }
436 else {
437 //usage();
438 fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg);
439 return 1;
440 }
441 }
442
443 if ( rootPath.empty()) {
444 fprintf(stderr, "-root should be specified\n");
445 return 1;
446 }
447 if (cacheDir.empty()) {
448 fprintf(stderr, "-cache_dir should be specified\n");
449 return 1;
450 }
451 // canonicalize rootPath
452 char resolvedPath[PATH_MAX];
453 if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
454 rootPath = resolvedPath;
455 }
456
457 // Find the boot volume so that we can ensure all overlays are on the same volume
458 struct stat rootStatBuf;
459 if ( stat(rootPath.c_str(), &rootStatBuf) != 0 ) {
460 fprintf(stderr, "update_dyld_sim_shared_cache: error: could not stat root file system because '%s'\n", strerror(errno));
461 return 1;
462 }
463
464 std::vector<std::string> pathPrefixes;
465
466 pathPrefixes.push_back(rootPath);
467
468 // build FileSystem object
469 const char* fsRoot = rootPath.empty() ? nullptr : rootPath.c_str();
470 dyld3::closure::FileSystemPhysical fileSystem(fsRoot, nullptr);
471
472
473 int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
474 if ( (err != 0) && (err != EEXIST) ) {
475 fprintf(stderr, "update_dyld_sim_shared_cache: could not access cache dir: mkpath_np(%s) failed errno=%d\n", cacheDir.c_str(), err);
476 return 1;
477 }
478
479 uint64_t t1 = mach_absolute_time();
480
481 __block std::vector<MappedMachOsByCategory> allFileSets;
482 switch ( platform ) {
483 case dyld3::Platform::iOS_simulator:
484 allFileSets.push_back({dyld3::GradedArchs::x86_64});
485 break;
486 case dyld3::Platform::watchOS_simulator:
487 allFileSets.push_back({dyld3::GradedArchs::i386});
488 break;
489 case dyld3::Platform::tvOS_simulator:
490 allFileSets.push_back({dyld3::GradedArchs::x86_64});
491 break;
492 default:
493 assert(0 && "invalid platform");
494 break;
495 }
496 findAllFiles(fileSystem, pathPrefixes, allFileSets, platform);
497 addMacOSHostLibs(allFileSets, platform);
498 addMacOSBinaries(fileSystem, pathPrefixes, allFileSets);
499
500 // nothing in OS uses i386 dylibs, so only dylibs used by third party apps need to be in cache
501 for (MappedMachOsByCategory& fileSet : allFileSets) {
502 pruneCachedDylibs(rootPath, skipDylibs, fileSet);
503 pruneOtherDylibs(rootPath, fileSet);
504 }
505
506 uint64_t t2 = mach_absolute_time();
507 if ( verbose ) {
508 fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
509 }
510
511 // build caches in parallel on machines with at leat 4GB of RAM
512 uint64_t memSize = 0;
513 size_t sz = sizeof(memSize);;
514 bool buildInParallel = false;
515 if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) {
516 if ( memSize >= 0x100000000ULL )
517 buildInParallel = true;
518 }
519 dispatch_queue_t dqueue = buildInParallel ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
520 : dispatch_queue_create("serial-queue", DISPATCH_QUEUE_SERIAL);
521
522 // build all caches
523 __block bool cacheBuildFailure = false;
524 __block bool wroteSomeCacheFile = false;
525 dispatch_apply(allFileSets.size(), dqueue, ^(size_t index) {
526 MappedMachOsByCategory& fileSet = allFileSets[index];
527 const std::string outFile = cacheDir + "/dyld_sim_shared_cache_" + fileSet.archs.name();
528
529 DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) {
530 if ( skipDylibs.count(runtimePath) )
531 return DyldSharedCache::MappedMachO();
532
533 for (const std::string& prefix : pathPrefixes) {
534 std::string fullPath = prefix + runtimePath;
535 struct stat statBuf;
536 if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
537 char truePath[PATH_MAX];
538 if ( realpath(fullPath.c_str(), truePath) != NULL ) {
539 std::string resolvedSymlink = truePath;
540 if ( !rootPath.empty() ) {
541 resolvedSymlink = resolvedSymlink.substr(rootPath.size());
542 }
543 if ( (runtimePath != resolvedSymlink) && !contains(runtimePath, "InputContext") ) { //HACK remove InputContext when fixed
544 // path requested is a symlink path, check if real path already loaded
545 for (const DyldSharedCache::MappedMachO& aDylibMapping : fileSet.dylibsForCache) {
546 if ( aDylibMapping.runtimePath == resolvedSymlink ) {
547 if ( verbose )
548 fprintf(stderr, "verifySelfContained, redirect %s to %s\n", runtimePath.c_str(), aDylibMapping.runtimePath.c_str());
549 return aDylibMapping;
550 }
551 }
552 }
553 }
554
555 std::vector<MappedMachOsByCategory> mappedFiles;
556 mappedFiles.push_back({fileSet.archs});
557 if ( addIfMachO(fileSystem, runtimePath, statBuf, mappedFiles, platform) ) {
558 if ( !mappedFiles.back().dylibsForCache.empty() ) {
559 if ( verbose )
560 fprintf(stderr, "verifySelfContained, add %s\n", mappedFiles.back().dylibsForCache.back().runtimePath.c_str());
561 return mappedFiles.back().dylibsForCache.back();
562 }
563 }
564 }
565 }
566 return DyldSharedCache::MappedMachO();
567 };
568 size_t startCount = fileSet.dylibsForCache.size();
569 std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>> excludes;
570 std::unordered_set<std::string> badZippered;
571 DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, badZippered, loader, excludes);
572 for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) {
573 fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s not in initial scan, but adding required dylib %s\n", fileSet.archs.name(), fileSet.dylibsForCache[i].runtimePath.c_str());
574 }
575 for (auto& exclude : excludes) {
576 std::string reasons = "(\"";
577 for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) {
578 reasons += *i;
579 if (i != --exclude.second.end()) {
580 reasons += "\", \"";
581 }
582 }
583 reasons += "\")";
584 fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archs.name(), exclude.first.runtimePath.c_str(), reasons.c_str());
585 fileSet.otherDylibsAndBundles.push_back(exclude.first);
586 }
587
588 // check if cache is already up to date
589 if ( !force ) {
590 if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) )
591 return;
592 }
593
594
595 // build cache new cache file
596 DyldSharedCache::CreateOptions options;
597 options.outputFilePath = outFile;
598 options.outputMapFilePath = cacheDir + "/dyld_sim_shared_cache_" + fileSet.archs.name() + ".map";
599 options.archs = &fileSet.archs;
600 options.platform = platform;
601 options.excludeLocalSymbols = false;
602 options.optimizeStubs = false;
603 options.optimizeObjC = true;
604 options.codeSigningDigestMode = DyldSharedCache::SHA256only;
605 options.dylibsRemovedDuringMastering = dylibsRemoved;
606 options.inodesAreSameAsRuntime = true;
607 options.cacheSupportsASLR = false;
608 options.forSimulator = true;
609 options.isLocallyBuiltCache = true;
610 options.verbose = verbose;
611 options.evictLeafDylibsOnOverflow = true;
612 options.dylibOrdering = parseOrderFile(dylibOrderFile);
613 options.dirtyDataSegmentOrdering = parseOrderFile(dirtyDataOrderFile);
614 DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSystem, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
615
616 // print any warnings
617 for (const std::string& warn : results.warnings) {
618 fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archs.name(), warn.c_str());
619 }
620 if ( results.errorMessage.empty() ) {
621 wroteSomeCacheFile = true;
622 }
623 else {
624 fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str());
625 cacheBuildFailure = true;
626 }
627 });
628
629 // we could unmap all input files, but tool is about to quit
630
631 return (cacheBuildFailure ? 1 : 0);
632 }
633