1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
3 * Copyright (c) 2018 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@
25 #include <sys/types.h>
28 #include <mach/mach.h>
29 #include <mach/mach_time.h>
30 #include <mach-o/dyld.h>
43 #include <sys/param.h>
44 #include <sys/sysctl.h>
45 #include <sys/resource.h>
49 #include <dispatch/dispatch.h>
50 #include <pthread/pthread.h>
51 #include <CoreFoundation/CoreFoundation.h>
55 #include <unordered_set>
56 #include <unordered_set>
61 #include "FileUtils.h"
62 #include "StringUtils.h"
63 #include "DyldSharedCache.h"
64 #include "MachOFile.h"
65 #include "MachOAnalyzer.h"
66 #include "ClosureFileSystemPhysical.h"
68 struct MappedMachOsByCategory
70 const dyld3
::GradedArchs
& archs
;
71 std
::vector
<DyldSharedCache
::MappedMachO
> dylibsForCache
;
72 std
::vector
<DyldSharedCache
::MappedMachO
> otherDylibsAndBundles
;
73 std
::vector
<DyldSharedCache
::MappedMachO
> mainExecutables
;
76 static const char* sAllowedPrefixes
[] = {
79 // don't look at main executables until simulator supports dyld3
84 static const char* sDontUsePrefixes
[] = {
89 static const char* sMacOSHostLibs
[] = {
90 "/usr/lib/system/libsystem_kernel.dylib",
91 "/usr/lib/system/libsystem_platform.dylib",
92 "/usr/lib/system/libsystem_pthread.dylib",
95 static const char* sMacOSBinaries
[] = {
96 "/sbin/launchd_sim_trampoline",
97 "/usr/sbin/iokitsimd",
98 "/usr/lib/system/host/liblaunch_sim.dylib",
101 static bool verbose
= false;
104 static bool addIfMachO(const dyld3
::closure
::FileSystem
& fileSystem
, const std
::string
& runtimePath
, const struct stat
& statBuf
, std
::vector
<MappedMachOsByCategory
>& files
, dyld3
::Platform platform
, Diagnostics
& diag
)
106 // don't precompute closure info for any debug or profile dylibs
107 if ( endsWith(runtimePath
, "_profile.dylib") || endsWith(runtimePath
, "_debug.dylib") || endsWith(runtimePath
, "_asan.dylib") || endsWith(runtimePath
, "_profile") || endsWith(runtimePath
, "_debug") )
109 if ( startsWith(runtimePath
, "/usr/lib/system/introspection/") )
113 for (MappedMachOsByCategory
& file
: files
) {
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
;
118 if ( ma
!= nullptr ) {
119 bool sipProtected
= false;
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
);
129 else if ( ma
->canBePlacedInDyldCache(runtimePath
.c_str(), ^(const char* msg
) {
130 diag
.error("Dylib located at '%s' cannot be placed in cache because: '%s'", loadedFileInfo
.path
, msg
);
132 file
.dylibsForCache
.emplace_back(runtimePath
, ma
, sliceLen
, issetuid
, sipProtected
, loadedFileInfo
.sliceOffset
, statBuf
.st_mtime
, statBuf
.st_ino
);
141 static void findAllFiles(const dyld3
::closure
::FileSystem
& fileSystem
, const std
::vector
<std
::string
>& pathPrefixes
, std
::vector
<MappedMachOsByCategory
>& files
, dyld3
::Platform platform
)
143 std
::unordered_set
<std
::string
> skipDirs
;
144 for (const char* s
: sDontUsePrefixes
)
147 __block std
::unordered_set
<std
::string
> alreadyUsed
;
148 bool multiplePrefixes
= (pathPrefixes
.size() > 1);
149 for (const std
::string
& prefix
: pathPrefixes
) {
150 // get all files from overlay for this search dir
151 for (const char* searchDir
: sAllowedPrefixes
) {
152 iterateDirectoryTree(prefix
, searchDir
, ^(const std
::string
& dirPath
) { return (skipDirs
.count(dirPath
) != 0); }, ^(const std
::string
& path
, const struct stat
& statBuf
) {
153 // ignore files that don't have 'x' bit set (all runnable mach-o files do)
154 const bool hasXBit
= ((statBuf
.st_mode
& S_IXOTH
) == S_IXOTH
);
155 if ( !hasXBit
&& !endsWith(path
, ".dylib") )
158 // ignore files too small (must have at least a page of TEXT and LINKEDIT)
159 if ( statBuf
.st_size
< 0x2000 )
162 // don't add paths already found using previous prefix
163 if ( multiplePrefixes
&& (alreadyUsed
.count(path
) != 0) )
166 // don't add binaries built for the host Mac OS platform
167 for (std
::string s
: sMacOSBinaries
) {
168 if (s
.compare(path
) == 0)
171 // if the file is mach-o, add to list
173 if ( addIfMachO(fileSystem
, path
, statBuf
, files
, platform
, diag
) ) {
174 if ( multiplePrefixes
)
175 alreadyUsed
.insert(path
);
182 static void addMacOSHostLibs(std
::vector
<MappedMachOsByCategory
>& allFileSets
, dyld3
::Platform platform
)
184 dyld3
::closure
::FileSystemPhysical fileSystem
;
185 for (const char* path
: sMacOSHostLibs
) {
187 if ( stat(path
, &statBuf
) == 0 ) {
189 addIfMachO(fileSystem
, path
, statBuf
, allFileSets
, dyld3
::Platform
::macOS
, diag
);
190 if ( diag
.hasError() )
191 fprintf(stderr
, "update_dyld_sim_shared_cache: warning: skipping %s because %s\n", path
, diag
.errorMessage().c_str());
196 static void addMacOSBinaries(const dyld3
::closure
::FileSystem
& fileSystem
, const std
::vector
<std
::string
>& pathPrefixes
, std
::vector
<MappedMachOsByCategory
>& files
)
198 for (const std
::string
& prefix
: pathPrefixes
) {
199 for (std
::string path
: sMacOSBinaries
) {
200 std
::string fullPath
= prefix
+ path
;
202 if ( stat(fullPath
.c_str(), &statBuf
) == 0 ) {
204 addIfMachO(fileSystem
, path
, statBuf
, files
, dyld3
::Platform
::macOS
, diag
);
205 if ( diag
.hasError() )
206 fprintf(stderr
, "update_dyld_sim_shared_cache: warning: skipping %s because %s\n", fullPath
.c_str(), diag
.errorMessage().c_str());
212 static bool dontCache(const std
::string
& simRuntimeRootPath
, const std
::string
& archName
,
213 const std
::unordered_set
<std
::string
>& pathsWithDuplicateInstallName
,
214 const DyldSharedCache
::MappedMachO
& aFile
, bool warn
,
215 const std
::unordered_set
<std
::string
>& skipDylibs
)
217 if ( skipDylibs
.count(aFile
.runtimePath
) )
219 if ( startsWith(aFile
.runtimePath
, "/usr/lib/system/introspection/") )
221 if ( startsWith(aFile
.runtimePath
, "/usr/local/") )
224 // anything inside a .app bundle is specific to app, so should not be in shared cache
225 if ( aFile
.runtimePath
.find(".app/") != std
::string
::npos
)
228 if ( archName
== "i386" ) {
229 if ( startsWith(aFile
.runtimePath
, "/System/Library/CoreServices/") )
231 if ( startsWith(aFile
.runtimePath
, "/System/Library/Extensions/") )
235 if ( aFile
.runtimePath
.find("//") != std
::string
::npos
) {
236 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());
240 const char* installName
= aFile
.mh
->installName();
241 if ( (pathsWithDuplicateInstallName
.count(aFile
.runtimePath
) != 0) && (aFile
.runtimePath
!= installName
) ) {
242 // <rdar://problem/46431467> if a dylib moves and a symlink is installed into its place, bom iterator will see both and issue a warning
244 bool isSymLink
= ( (lstat(aFile
.runtimePath
.c_str(), &statBuf
) == 0) && S_ISLNK(statBuf
.st_mode
) );
245 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());
249 if ( aFile
.runtimePath
!= installName
) {
250 // see if install name is a symlink to actual path
251 std
::string fullInstall
= simRuntimeRootPath
+ installName
;
252 char resolvedPath
[PATH_MAX
];
253 if ( realpath(fullInstall
.c_str(), resolvedPath
) != NULL
) {
254 std
::string resolvedSymlink
= resolvedPath
;
255 if ( !simRuntimeRootPath
.empty() ) {
256 resolvedSymlink
= resolvedSymlink
.substr(simRuntimeRootPath
.size());
258 if ( aFile
.runtimePath
== resolvedSymlink
) {
262 // <rdar://problem/38000411> also if runtime path is a symlink to install name
263 std
::string fullRuntime
= simRuntimeRootPath
+ aFile
.runtimePath
;
264 if ( realpath(fullRuntime
.c_str(), resolvedPath
) != NULL
) {
265 std
::string resolvedSymlink
= resolvedPath
;
266 if ( !simRuntimeRootPath
.empty() ) {
267 resolvedSymlink
= resolvedSymlink
.substr(simRuntimeRootPath
.size());
269 if ( resolvedSymlink
== installName
) {
273 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());
279 static void pruneCachedDylibs(const std
::string
& volumePrefix
, const std
::unordered_set
<std
::string
>& skipDylibs
, MappedMachOsByCategory
& fileSet
)
281 std
::unordered_set
<std
::string
> pathsWithDuplicateInstallName
;
283 std
::unordered_map
<std
::string
, std
::string
> installNameToFirstPath
;
284 for (DyldSharedCache
::MappedMachO
& aFile
: fileSet
.dylibsForCache
) {
285 const char* installName
= aFile
.mh
->installName();
286 auto pos
= installNameToFirstPath
.find(installName
);
287 if ( pos
== installNameToFirstPath
.end() ) {
288 installNameToFirstPath
[installName
] = aFile
.runtimePath
;
291 pathsWithDuplicateInstallName
.insert(aFile
.runtimePath
);
292 pathsWithDuplicateInstallName
.insert(installNameToFirstPath
[installName
]);
296 for (DyldSharedCache
::MappedMachO
& aFile
: fileSet
.dylibsForCache
) {
297 if ( dontCache(volumePrefix
, fileSet
.archs
.name(), pathsWithDuplicateInstallName
, aFile
, true, skipDylibs
) ){
298 // <rdar://problem/46423929> don't build dlopen closures for symlinks to something in the dyld cache
299 if ( pathsWithDuplicateInstallName
.count(aFile
.runtimePath
) == 0 )
300 fileSet
.otherDylibsAndBundles
.push_back(aFile
);
303 fileSet
.dylibsForCache
.erase(std
::remove_if(fileSet
.dylibsForCache
.begin(), fileSet
.dylibsForCache
.end(),
304 [&](const DyldSharedCache
::MappedMachO
& aFile
) { return dontCache(volumePrefix
, fileSet
.archs
.name(), pathsWithDuplicateInstallName
, aFile
, false, skipDylibs
); }),
305 fileSet
.dylibsForCache
.end());
308 static void pruneOtherDylibs(const std
::string
& volumePrefix
, MappedMachOsByCategory
& fileSet
)
310 // other OS dylibs should not contain dylibs that are embedded in some .app bundle
311 fileSet
.otherDylibsAndBundles
.erase(std
::remove_if(fileSet
.otherDylibsAndBundles
.begin(), fileSet
.otherDylibsAndBundles
.end(),
312 [&](const DyldSharedCache
::MappedMachO
& aFile
) { return (aFile
.runtimePath
.find(".app/") != std
::string
::npos
); }),
313 fileSet
.otherDylibsAndBundles
.end());
317 static std
::string
getOrderFileContent(const std
::string
& orderFile
)
319 std
::ifstream
fstream(orderFile
);
320 std
::stringstream stringBuf
;
321 stringBuf
<< fstream
.rdbuf();
322 return stringBuf
.str();
325 static bool existingCacheUpToDate(const std
::string
& existingCache
, const std
::vector
<DyldSharedCache
::MappedMachO
>& currentDylibs
)
327 // if no existing cache, it is not up-to-date
328 int fd
= ::open(existingCache
.c_str(), O_RDONLY
);
332 if ( ::fstat(fd
, &statbuf
) == -1 ) {
337 // build map of found dylibs
338 std
::unordered_map
<std
::string
, const DyldSharedCache
::MappedMachO
*> currentDylibMap
;
339 for (const DyldSharedCache
::MappedMachO
& aFile
: currentDylibs
) {
340 //fprintf(stderr, "0x%0llX 0x%0llX %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str());
341 currentDylibMap
[aFile
.runtimePath
] = &aFile
;
344 // make sure all dylibs in existing cache have same mtime and inode as found dylib
345 __block
bool foundMismatch
= false;
346 const uint64_t cacheMapLen
= statbuf
.st_size
;
347 void *p
= ::mmap(NULL
, cacheMapLen
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
348 if ( p
!= MAP_FAILED
) {
349 const DyldSharedCache
* cache
= (DyldSharedCache
*)p
;
350 cache
->forEachImageEntry(^(const char* installName
, uint64_t mTime
, uint64_t inode
) {
351 bool foundMatch
= false;
352 auto pos
= currentDylibMap
.find(installName
);
353 if ( pos
!= currentDylibMap
.end() ) {
354 const DyldSharedCache
::MappedMachO
* foundDylib
= pos
->second
;
355 if ( (foundDylib
->inode
== inode
) && (foundDylib
->modTime
== mTime
) ) {
360 // use slow path and look for any dylib with a matching inode and mtime
361 bool foundSlow
= false;
362 for (const DyldSharedCache
::MappedMachO
& aFile
: currentDylibs
) {
363 if ( (aFile
.inode
== inode
) && (aFile
.modTime
== mTime
) ) {
369 foundMismatch
= true;
371 fprintf(stderr
, "rebuilding dyld cache because dylib changed: %s\n", installName
);
375 ::munmap(p
, cacheMapLen
);
380 return !foundMismatch
;
383 static void addFileSets(const std
::unordered_set
<std
::string
>& allowedArchs
, std
::unordered_set
<std
::string
>& requestedArchs
, std
::vector
<MappedMachOsByCategory
>& fileSets
)
385 if ( requestedArchs
.empty() ) {
387 requestedArchs
.insert("arm64");
389 requestedArchs
.insert("x86_64");
390 requestedArchs
.insert("i386");
392 #error unknown platform
396 for (auto& requested
: requestedArchs
) {
397 if ( allowedArchs
.find(requested
) != allowedArchs
.end() ) {
398 const dyld3
::GradedArchs
& archs
= dyld3
::GradedArchs
::forName(requested
.c_str(), false);
399 fileSets
.push_back({archs
});
404 inline uint32_t absolutetime_to_milliseconds(uint64_t abstime
)
406 return (uint32_t)(abstime
/1000/1000);
410 #define TERMINATE_IF_LAST_ARG( s ) \
412 if ( i == argc - 1 ) { \
413 fprintf(stderr, s ); \
418 int main(int argc
, const char* argv
[], const char* envp
[])
420 std
::string rootPath
;
422 bool dylibsRemoved
= false;
423 std
::string cacheDir
;
424 std
::string dylibOrderFile
;
425 std
::string dirtyDataOrderFile
;
426 dyld3
::Platform platform
= dyld3
::Platform
::iOS_simulator
;
427 std
::unordered_set
<std
::string
> skipDylibs
;
428 std
::unordered_set
<std
::string
> requestedArchs
;
430 // parse command line options
431 for (int i
= 1; i
< argc
; ++i
) {
432 const char* arg
= argv
[i
];
433 if (strcmp(arg
, "-debug") == 0) {
436 else if (strcmp(arg
, "-verbose") == 0) {
439 else if ((strcmp(arg
, "-root") == 0) || (strcmp(arg
, "--root") == 0)) {
440 TERMINATE_IF_LAST_ARG("-root missing path argument\n");
441 rootPath
= argv
[++i
];
443 else if (strcmp(arg
, "-cache_dir") == 0) {
444 TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n");
445 cacheDir
= argv
[++i
];
447 else if (strcmp(arg
, "-iOS") == 0) {
448 platform
= dyld3
::Platform
::iOS_simulator
;
450 else if (strcmp(arg
, "-watchOS") == 0) {
451 platform
= dyld3
::Platform
::watchOS_simulator
;
453 else if (strcmp(arg
, "-tvOS") == 0) {
454 platform
= dyld3
::Platform
::tvOS_simulator
;
456 else if (strcmp(arg
, "-dylibs_removed_in_mastering") == 0) {
457 dylibsRemoved
= true;
459 else if (strcmp(arg
, "-dylib_order_file") == 0) {
460 TERMINATE_IF_LAST_ARG("-dylib_order_file missing path argument\n");
461 dylibOrderFile
= argv
[++i
];
463 else if (strcmp(arg
, "-dirty_data_order_file") == 0) {
464 TERMINATE_IF_LAST_ARG("-dirty_data_order_file missing path argument\n");
465 dirtyDataOrderFile
= argv
[++i
];
467 else if (strcmp(arg
, "-arch") == 0) {
468 TERMINATE_IF_LAST_ARG("-arch missing arch argument\n");
469 requestedArchs
.insert(argv
[++i
]);
471 else if (strcmp(arg
, "-force") == 0) {
474 else if (strcmp(arg
, "-skip") == 0) {
475 TERMINATE_IF_LAST_ARG("-skip missing argument\n");
476 skipDylibs
.insert(argv
[++i
]);
480 fprintf(stderr
, "update_dyld_sim_shared_cache: unknown option: %s\n", arg
);
485 if ( rootPath
.empty() ) {
486 fprintf(stderr
, "-root should be specified\n");
489 if ( cacheDir
.empty() ) {
490 fprintf(stderr
, "-cache_dir should be specified\n");
493 // canonicalize rootPath
494 char resolvedPath
[PATH_MAX
];
495 if ( realpath(rootPath
.c_str(), resolvedPath
) != NULL
) {
496 rootPath
= resolvedPath
;
499 // canonicalize cacheDir.
500 // Later, path is checked against real path name before writing cache file to avoid TOCTU race condition.
501 if ( realpath(cacheDir
.c_str(), resolvedPath
) != NULL
) {
502 cacheDir
= resolvedPath
;
505 // Find the boot volume so that we can ensure all overlays are on the same volume
506 struct stat rootStatBuf
;
507 if ( stat(rootPath
.c_str(), &rootStatBuf
) != 0 ) {
508 fprintf(stderr
, "update_dyld_sim_shared_cache: error: could not stat root file system because '%s'\n", strerror(errno
));
512 std
::vector
<std
::string
> pathPrefixes
;
514 pathPrefixes
.push_back(rootPath
);
516 // build FileSystem object
517 const char* fsRoot
= rootPath
.empty() ?
nullptr : rootPath
.c_str();
518 dyld3
::closure
::FileSystemPhysical
fileSystem(fsRoot
, nullptr);
521 int err
= mkpath_np(cacheDir
.c_str(), S_IRWXU
| S_IRGRP
|S_IXGRP
| S_IROTH
|S_IXOTH
);
522 if ( (err
!= 0) && (err
!= EEXIST
) ) {
523 fprintf(stderr
, "update_dyld_sim_shared_cache: could not access cache dir: mkpath_np(%s) failed errno=%d\n", cacheDir
.c_str(), err
);
527 std
::string dylibOrderFileContent
;
528 if ( !dylibOrderFile
.empty() ) {
529 dylibOrderFileContent
= getOrderFileContent(dylibOrderFile
);
532 std
::string dirtyDataOrderFileContent
;
533 if ( !dirtyDataOrderFile
.empty() ) {
534 dirtyDataOrderFileContent
= getOrderFileContent(dirtyDataOrderFile
);
536 uint64_t t1
= mach_absolute_time();
538 __block std
::vector
<MappedMachOsByCategory
> allFileSets
;
539 std
::unordered_set
<std
::string
> allowedArchs
;
540 switch ( platform
) {
541 case dyld3
::Platform
::iOS_simulator
:
542 allowedArchs
.insert("x86_64");
543 allowedArchs
.insert("arm64");
545 case dyld3
::Platform
::watchOS_simulator
:
546 allowedArchs
.insert("i386");
547 allowedArchs
.insert("x86_64");
548 allowedArchs
.insert("arm64");
551 case dyld3
::Platform
::tvOS_simulator
:
552 allowedArchs
.insert("x86_64");
553 allowedArchs
.insert("arm64");
556 assert(0 && "invalid platform");
560 addFileSets(allowedArchs
, requestedArchs
, allFileSets
);
561 if ( allFileSets
.empty() ) {
562 fprintf(stderr
, "update_dyld_sim_shared_cache: error: no valid architecture specified\n");
566 findAllFiles(fileSystem
, pathPrefixes
, allFileSets
, platform
);
567 addMacOSHostLibs(allFileSets
, platform
);
568 addMacOSBinaries(fileSystem
, pathPrefixes
, allFileSets
);
570 // nothing in OS uses i386 dylibs, so only dylibs used by third party apps need to be in cache
571 for (MappedMachOsByCategory
& fileSet
: allFileSets
) {
572 pruneCachedDylibs(rootPath
, skipDylibs
, fileSet
);
573 pruneOtherDylibs(rootPath
, fileSet
);
576 uint64_t t2
= mach_absolute_time();
578 fprintf(stderr
, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2
-t1
));
581 // build caches in parallel on machines with at leat 4GB of RAM
582 uint64_t memSize
= 0;
583 size_t sz
= sizeof(memSize
);;
584 bool buildInParallel
= false;
585 if ( sysctlbyname("hw.memsize", &memSize
, &sz
, NULL
, 0) == 0 ) {
586 if ( memSize
>= 0x100000000ULL
)
587 buildInParallel
= true;
589 dispatch_queue_t dqueue
= buildInParallel ?
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0)
590 : dispatch_queue_create("serial-queue", DISPATCH_QUEUE_SERIAL
);
593 __block
bool cacheBuildFailure
= false;
594 __block
bool wroteSomeCacheFile
= false;
595 dispatch_apply(allFileSets
.size(), dqueue
, ^(size_t index
) {
596 MappedMachOsByCategory
& fileSet
= allFileSets
[index
];
597 const std
::string outFile
= cacheDir
+ "/dyld_sim_shared_cache_" + fileSet
.archs
.name();
599 DyldSharedCache
::MappedMachO (^loader
)(const std
::string
&, Diagnostics
&) = ^DyldSharedCache
::MappedMachO(const std
::string
& runtimePath
, Diagnostics
& diag
) {
600 if ( skipDylibs
.count(runtimePath
) )
601 return DyldSharedCache
::MappedMachO();
603 for (const std
::string
& prefix
: pathPrefixes
) {
604 std
::string fullPath
= prefix
+ runtimePath
;
606 if ( stat(fullPath
.c_str(), &statBuf
) == 0 ) {
607 char truePath
[PATH_MAX
];
608 if ( realpath(fullPath
.c_str(), truePath
) != NULL
) {
609 std
::string resolvedSymlink
= truePath
;
610 if ( !rootPath
.empty() ) {
611 resolvedSymlink
= resolvedSymlink
.substr(rootPath
.size());
613 if ( (runtimePath
!= resolvedSymlink
) && !contains(runtimePath
, "InputContext") ) { //HACK remove InputContext when fixed
614 // path requested is a symlink path, check if real path already loaded
615 for (const DyldSharedCache
::MappedMachO
& aDylibMapping
: fileSet
.dylibsForCache
) {
616 if ( aDylibMapping
.runtimePath
== resolvedSymlink
) {
618 fprintf(stderr
, "verifySelfContained, redirect %s to %s\n", runtimePath
.c_str(), aDylibMapping
.runtimePath
.c_str());
619 return aDylibMapping
;
625 std
::vector
<MappedMachOsByCategory
> mappedFiles
;
626 mappedFiles
.push_back({fileSet
.archs
});
627 if ( addIfMachO(fileSystem
, runtimePath
, statBuf
, mappedFiles
, platform
, diag
) ) {
628 if ( !mappedFiles
.back().dylibsForCache
.empty() ) {
630 fprintf(stderr
, "verifySelfContained, add %s\n", mappedFiles
.back().dylibsForCache
.back().runtimePath
.c_str());
631 return mappedFiles
.back().dylibsForCache
.back();
636 return DyldSharedCache
::MappedMachO();
638 size_t startCount
= fileSet
.dylibsForCache
.size();
639 std
::vector
<std
::pair
<DyldSharedCache
::MappedMachO
, std
::set
<std
::string
>>> excludes
;
640 std
::unordered_set
<std
::string
> badZippered
;
641 DyldSharedCache
::verifySelfContained(fileSet
.dylibsForCache
, badZippered
, loader
, excludes
);
642 for (size_t i
=startCount
; i
< fileSet
.dylibsForCache
.size(); ++i
) {
643 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());
645 for (auto& exclude
: excludes
) {
646 std
::string reasons
= "(\"";
647 for (auto i
= exclude
.second
.begin(); i
!= exclude
.second
.end(); ++i
) {
649 if (i
!= --exclude
.second
.end()) {
654 fprintf(stderr
, "update_dyld_sim_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet
.archs
.name(), exclude
.first
.runtimePath
.c_str(), reasons
.c_str());
655 fileSet
.otherDylibsAndBundles
.push_back(exclude
.first
);
658 // check if cache is already up to date
660 if ( existingCacheUpToDate(outFile
, fileSet
.dylibsForCache
) )
665 // build cache new cache file
666 DyldSharedCache
::CreateOptions options
;
667 options
.outputFilePath
= outFile
;
668 options
.outputMapFilePath
= cacheDir
+ "/dyld_sim_shared_cache_" + fileSet
.archs
.name() + ".map";
669 options
.archs
= &fileSet
.archs
;
670 options
.platform
= platform
;
671 options
.localSymbolMode
= DyldSharedCache
::LocalSymbolsMode
::keep
;
672 options
.optimizeStubs
= false;
673 options
.optimizeDyldDlopens
= false; // don't add dyld3 closures to simulator cache
674 options
.optimizeDyldLaunches
= false; // don't add dyld3 closures to simulator cache
675 options
.codeSigningDigestMode
= DyldSharedCache
::SHA256only
;
676 options
.dylibsRemovedDuringMastering
= dylibsRemoved
;
677 options
.inodesAreSameAsRuntime
= true;
678 options
.cacheSupportsASLR
= false;
679 options
.forSimulator
= true;
680 options
.isLocallyBuiltCache
= true;
681 options
.verbose
= verbose
;
682 options
.evictLeafDylibsOnOverflow
= true;
683 options
.dylibOrdering
= parseOrderFile(dylibOrderFileContent
);
684 options
.dirtyDataSegmentOrdering
= parseOrderFile(dirtyDataOrderFileContent
);
685 DyldSharedCache
::CreateResults results
= DyldSharedCache
::create(options
, fileSystem
, fileSet
.dylibsForCache
, fileSet
.otherDylibsAndBundles
, fileSet
.mainExecutables
);
687 // print any warnings
688 for (const std
::string
& warn
: results
.warnings
) {
689 fprintf(stderr
, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet
.archs
.name(), warn
.c_str());
691 if ( results
.errorMessage
.empty() ) {
692 wroteSomeCacheFile
= true;
695 fprintf(stderr
, "update_dyld_sim_shared_cache: %s\n", results
.errorMessage
.c_str());
696 cacheBuildFailure
= true;
700 // we could unmap all input files, but tool is about to quit
702 return (cacheBuildFailure ?
1 : 0);