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 <sys/types.h>
32 #include <mach/mach.h>
33 #include <mach/mach_vm.h>
34 #include <mach/mach_time.h>
35 #include <mach/shared_region.h>
36 #include <apfs/apfs_fsctl.h>
39 #include <CommonCrypto/CommonHMAC.h>
40 #include <CommonCrypto/CommonDigest.h>
41 #include <CommonCrypto/CommonDigestSPI.h>
43 #include "mach-o/dyld_priv.h"
44 #include "ClosureBuilder.h"
46 #include "ClosureFileSystemNull.h"
47 #include "CodeSigningTypes.h"
48 #include "MachOFileAbstraction.hpp"
49 #include "SharedCacheBuilder.h"
50 #include "RootsChecker.h"
51 #include "IMPCachesBuilder.hpp"
53 #include "FileUtils.h"
54 #include "StringUtils.h"
57 #if __has_include("dyld_cache_config.h")
58 #include "dyld_cache_config.h"
60 #define ARM_SHARED_REGION_START 0x1A000000ULL
61 #define ARM_SHARED_REGION_SIZE 0x26000000ULL
62 #define ARM64_SHARED_REGION_START 0x180000000ULL
63 #define ARM64_SHARED_REGION_SIZE 0x100000000ULL
66 #if ARM64_SHARED_REGION_START == 0x7FFF00000000
67 #define ARM64_DELTA_MASK 0x00FF000000000000
69 #define ARM64_DELTA_MASK 0x00FFFF0000000000
72 #ifndef ARM64_32_SHARED_REGION_START
73 #define ARM64_32_SHARED_REGION_START 0x1A000000ULL
74 #define ARM64_32_SHARED_REGION_SIZE 0x26000000ULL
77 #define ARMV7K_CHAIN_BITS 0xC0000000
79 #if BUILDING_UPDATE_DYLD_CACHE_BUILDER
80 #define DISCONTIGUOUS_RX 0x7FFF20000000ULL
82 #define DISCONTIGUOUS_RX 0x7FFF20000000ULL // size for MRM builder
84 #define DISCONTIGUOUS_RW 0x7FFF80000000ULL
85 #define DISCONTIGUOUS_RO 0x7FFFC0000000ULL
86 #define DISCONTIGUOUS_RX_SIZE (DISCONTIGUOUS_RW - DISCONTIGUOUS_RX)
87 #define DISCONTIGUOUS_RW_SIZE 0x40000000
88 #define DISCONTIGUOUS_RO_SIZE 0x3FE00000
90 const SharedCacheBuilder::ArchLayout
SharedCacheBuilder::_s_archLayout
[] = {
91 { DISCONTIGUOUS_RX
, 0xEFE00000ULL
, 0x40000000, 0x00FFFF0000000000, "x86_64", CS_PAGE_SIZE_4K
, 14, 2, true, true, true },
92 { DISCONTIGUOUS_RX
, 0xEFE00000ULL
, 0x40000000, 0x00FFFF0000000000, "x86_64h", CS_PAGE_SIZE_4K
, 14, 2, true, true, true },
93 { SHARED_REGION_BASE_I386
, SHARED_REGION_SIZE_I386
, 0x00200000, 0x0, "i386", CS_PAGE_SIZE_4K
, 12, 0, false, false, true },
94 { ARM64_SHARED_REGION_START
, ARM64_SHARED_REGION_SIZE
, 0x02000000, ARM64_DELTA_MASK
, "arm64", CS_PAGE_SIZE_4K
, 14, 2, false, true, false },
95 #if SUPPORT_ARCH_arm64e
96 { ARM64_SHARED_REGION_START
, ARM64_SHARED_REGION_SIZE
, 0x02000000, ARM64_DELTA_MASK
, "arm64e", CS_PAGE_SIZE_16K
, 14, 2, false, true, false },
98 #if SUPPORT_ARCH_arm64_32
99 { ARM64_32_SHARED_REGION_START
, ARM64_32_SHARED_REGION_SIZE
, 0x02000000, 0xC0000000, "arm64_32", CS_PAGE_SIZE_16K
, 14, 6, false, false, true },
101 { ARM_SHARED_REGION_START
, ARM_SHARED_REGION_SIZE
, 0x02000000, 0xE0000000, "armv7s", CS_PAGE_SIZE_4K
, 14, 4, false, false, true },
102 { ARM_SHARED_REGION_START
, ARM_SHARED_REGION_SIZE
, 0x00400000, ARMV7K_CHAIN_BITS
, "armv7k", CS_PAGE_SIZE_4K
, 14, 4, false, false, true },
103 { 0x40000000, 0x40000000, 0x02000000, 0x0, "sim-x86", CS_PAGE_SIZE_4K
, 14, 0, false, false, true }
106 // These are functions that are interposed by Instruments.app or ASan
107 const char* const SharedCacheBuilder::_s_neverStubEliminateSymbols
[] = {
112 "__objc_autoreleasePoolPop",
132 "_dispatch_barrier_async_f",
133 "_dispatch_group_async",
134 "_dispatch_group_async_f",
135 "_dispatch_source_set_cancel_handler",
136 "_dispatch_source_set_event_handler",
215 "_malloc_create_zone",
216 "_malloc_default_purgeable_zone",
217 "_malloc_default_zone",
218 "_malloc_destroy_zone",
220 "_malloc_make_nonpurgeable",
221 "_malloc_make_purgeable",
222 "_malloc_set_zone_name",
223 "_malloc_zone_from_ptr",
240 "_objc_autoreleasePoolPop",
242 "_objc_setProperty_atomic",
243 "_objc_setProperty_atomic_copy",
244 "_objc_setProperty_nonatomic",
245 "_objc_setProperty_nonatomic_copy",
253 "_pthread_attr_getdetachstate",
254 "_pthread_attr_getguardsize",
255 "_pthread_attr_getinheritsched",
256 "_pthread_attr_getschedparam",
257 "_pthread_attr_getschedpolicy",
258 "_pthread_attr_getscope",
259 "_pthread_attr_getstack",
260 "_pthread_attr_getstacksize",
261 "_pthread_condattr_getpshared",
263 "_pthread_getschedparam",
265 "_pthread_mutex_lock",
266 "_pthread_mutex_unlock",
267 "_pthread_mutexattr_getprioceiling",
268 "_pthread_mutexattr_getprotocol",
269 "_pthread_mutexattr_getpshared",
270 "_pthread_mutexattr_gettype",
271 "_pthread_rwlockattr_getpshared",
361 // <rdar://problem/22050956> always use stubs for C++ symbols that can be overridden
371 inline uint32_t absolutetime_to_milliseconds(uint64_t abstime
)
373 return (uint32_t)(abstime
/1000/1000);
376 // Handles building a list of input files to the SharedCacheBuilder itself.
377 class CacheInputBuilder
{
379 CacheInputBuilder(const dyld3::closure::FileSystem
& fileSystem
,
380 const dyld3::GradedArchs
& archs
, dyld3::Platform reqPlatform
)
381 : fileSystem(fileSystem
), reqArchs(archs
), reqPlatform(reqPlatform
) { }
383 // Loads and maps any MachOs in the given list of files.
384 void loadMachOs(std::vector
<CacheBuilder::InputFile
>& inputFiles
,
385 std::vector
<CacheBuilder::LoadedMachO
>& dylibsToCache
,
386 std::vector
<CacheBuilder::LoadedMachO
>& otherDylibs
,
387 std::vector
<CacheBuilder::LoadedMachO
>& executables
,
388 std::vector
<CacheBuilder::LoadedMachO
>& couldNotLoadFiles
) {
390 std::map
<std::string
, uint64_t> dylibInstallNameMap
;
391 for (CacheBuilder::InputFile
& inputFile
: inputFiles
) {
392 char realerPath
[MAXPATHLEN
];
393 dyld3::closure::LoadedFileInfo loadedFileInfo
= dyld3::MachOAnalyzer::load(inputFile
.diag
, fileSystem
, inputFile
.path
, reqArchs
, reqPlatform
, realerPath
);
394 if ( (reqPlatform
== dyld3::Platform::macOS
) && inputFile
.diag
.hasError() ) {
395 // Try again with iOSMac
396 inputFile
.diag
.clearError();
397 loadedFileInfo
= dyld3::MachOAnalyzer::load(inputFile
.diag
, fileSystem
, inputFile
.path
, reqArchs
, dyld3::Platform::iOSMac
, realerPath
);
399 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)loadedFileInfo
.fileContent
;
401 couldNotLoadFiles
.emplace_back((CacheBuilder::LoadedMachO
){ DyldSharedCache::MappedMachO(), loadedFileInfo
, &inputFile
});
405 DyldSharedCache::MappedMachO
mappedFile(inputFile
.path
, ma
, loadedFileInfo
.sliceLen
, false, false,
406 loadedFileInfo
.sliceOffset
, loadedFileInfo
.mtime
, loadedFileInfo
.inode
);
408 // The file can be loaded with the given slice, but we may still want to exlude it from the cache.
410 std::string installName
= ma
->installName();
412 const char* dylibPath
= inputFile
.path
;
413 if ( (installName
!= inputFile
.path
) && (reqPlatform
== dyld3::Platform::macOS
) ) {
414 // We now typically require that install names and paths match. However symlinks may allow us to bring in a path which
415 // doesn't match its install name.
417 // /usr/lib/libstdc++.6.0.9.dylib is a real file with install name /usr/lib/libstdc++.6.dylib
418 // /usr/lib/libstdc++.6.dylib is a symlink to /usr/lib/libstdc++.6.0.9.dylib
419 // So long as we add both paths (with one as an alias) then this will work, even if dylibs are removed from disk
420 // but the symlink remains.
421 char resolvedSymlinkPath
[PATH_MAX
];
422 if ( fileSystem
.getRealPath(installName
.c_str(), resolvedSymlinkPath
) ) {
423 if (!strcmp(resolvedSymlinkPath
, inputFile
.path
)) {
424 // Symlink is the install name and points to the on-disk dylib
425 //fprintf(stderr, "Symlink works: %s == %s\n", inputFile.path, installName.c_str());
426 dylibPath
= installName
.c_str();
431 if (!ma
->canBePlacedInDyldCache(dylibPath
, ^(const char* msg
) {
432 inputFile
.diag
.warning("Dylib located at '%s' cannot be placed in cache because: %s", inputFile
.path
, msg
);
435 if (!ma
->canHavePrecomputedDlopenClosure(inputFile
.path
, ^(const char* msg
) {
436 inputFile
.diag
.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile
.path
, msg
);
438 fileSystem
.unloadFile(loadedFileInfo
);
441 otherDylibs
.emplace_back((CacheBuilder::LoadedMachO
){ mappedFile
, loadedFileInfo
, &inputFile
});
445 // Otherwise see if we have another file with this install name
446 auto iteratorAndInserted
= dylibInstallNameMap
.insert(std::make_pair(installName
, dylibsToCache
.size()));
447 if (iteratorAndInserted
.second
) {
448 // We inserted the dylib so we haven't seen another with this name.
449 if (installName
[0] != '@' && installName
!= inputFile
.path
) {
450 inputFile
.diag
.warning("Dylib located at '%s' has installname '%s'", inputFile
.path
, installName
.c_str());
453 dylibsToCache
.emplace_back((CacheBuilder::LoadedMachO
){ mappedFile
, loadedFileInfo
, &inputFile
});
455 // We didn't insert this one so we've seen it before.
456 CacheBuilder::LoadedMachO
& previousLoadedMachO
= dylibsToCache
[iteratorAndInserted
.first
->second
];
457 inputFile
.diag
.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName
.c_str(), inputFile
.path
, previousLoadedMachO
.mappedFile
.runtimePath
.c_str());
459 // This is the "Good" one, overwrite
460 if (inputFile
.path
== installName
) {
461 // Unload the old one
462 fileSystem
.unloadFile(previousLoadedMachO
.loadedFileInfo
);
464 // And replace with this one.
465 previousLoadedMachO
.mappedFile
= mappedFile
;
466 previousLoadedMachO
.loadedFileInfo
= loadedFileInfo
;
469 } else if (ma
->isBundle()) {
471 if (!ma
->canHavePrecomputedDlopenClosure(inputFile
.path
, ^(const char* msg
) {
472 inputFile
.diag
.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile
.path
, msg
);
474 fileSystem
.unloadFile(loadedFileInfo
);
477 otherDylibs
.emplace_back((CacheBuilder::LoadedMachO
){ mappedFile
, loadedFileInfo
, &inputFile
});
478 } else if (ma
->isDynamicExecutable()) {
480 // Let the platform exclude the file before we do anything else.
481 if (platformExcludesExecutablePath(inputFile
.path
)) {
482 inputFile
.diag
.verbose("Platform excluded file\n");
483 fileSystem
.unloadFile(loadedFileInfo
);
486 executables
.emplace_back((CacheBuilder::LoadedMachO
){ mappedFile
, loadedFileInfo
, &inputFile
});
488 inputFile
.diag
.verbose("Unsupported mach file type\n");
489 fileSystem
.unloadFile(loadedFileInfo
);
496 static bool platformExcludesExecutablePath_macOS(const std::string
& path
) {
497 // We no longer support ROSP, so skip all paths which start with the special prefix
498 if ( startsWith(path
, "/System/Library/Templates/Data/") )
501 static const char* sAllowedPrefixes
[] = {
506 "/Library/Apple/System/",
507 "/Library/Apple/usr/",
508 "/System/Applications/Safari.app/",
509 "/Library/CoreMediaIO/Plug-Ins/DAL/" // temp until plugins moved or closured working
512 bool inSearchDir
= false;
513 for (const char* searchDir
: sAllowedPrefixes
) {
514 if ( strncmp(searchDir
, path
.c_str(), strlen(searchDir
)) == 0 ) {
523 // Returns true if the current platform requires that this path be excluded from the shared cache
524 // Note that this overrides any exclusion from anywhere else.
525 bool platformExcludesExecutablePath(const std::string
& path
) {
526 switch (reqPlatform
) {
527 case dyld3::Platform::unknown
:
529 case dyld3::Platform::macOS
:
530 return platformExcludesExecutablePath_macOS(path
);
531 case dyld3::Platform::iOS
:
533 case dyld3::Platform::tvOS
:
535 case dyld3::Platform::watchOS
:
537 case dyld3::Platform::bridgeOS
:
539 case dyld3::Platform::iOSMac
:
540 return platformExcludesExecutablePath_macOS(path
);
541 case dyld3::Platform::iOS_simulator
:
543 case dyld3::Platform::tvOS_simulator
:
545 case dyld3::Platform::watchOS_simulator
:
547 case dyld3::Platform::driverKit
:
552 const dyld3::closure::FileSystem
& fileSystem
;
553 const dyld3::GradedArchs
& reqArchs
;
554 dyld3::Platform reqPlatform
;
557 SharedCacheBuilder::SharedCacheBuilder(const DyldSharedCache::CreateOptions
& options
,
558 const dyld3::closure::FileSystem
& fileSystem
)
559 : CacheBuilder(options
, fileSystem
) {
561 std::string targetArch
= options
.archs
->name();
562 if ( options
.forSimulator
&& (options
.archs
== &dyld3::GradedArchs::i386
) )
563 targetArch
= "sim-x86";
565 for (const ArchLayout
& layout
: _s_archLayout
) {
566 if ( layout
.archName
== targetArch
) {
567 _archLayout
= &layout
;
568 _is64
= _archLayout
->is64
;
574 _diagnostics
.error("Tool was built without support for: '%s'", targetArch
.c_str());
578 static void verifySelfContained(const dyld3::closure::FileSystem
& fileSystem
,
579 std::vector
<CacheBuilder::LoadedMachO
>& dylibsToCache
,
580 std::vector
<CacheBuilder::LoadedMachO
>& otherDylibs
,
581 std::vector
<CacheBuilder::LoadedMachO
>& couldNotLoadFiles
)
583 // build map of dylibs
584 __block
std::map
<std::string
, const CacheBuilder::LoadedMachO
*> knownDylibs
;
585 __block
std::map
<std::string
, const CacheBuilder::LoadedMachO
*> allDylibs
;
586 for (const CacheBuilder::LoadedMachO
& dylib
: dylibsToCache
) {
587 knownDylibs
.insert({ dylib
.mappedFile
.runtimePath
, &dylib
});
588 allDylibs
.insert({ dylib
.mappedFile
.runtimePath
, &dylib
});
589 if (const char* installName
= dylib
.mappedFile
.mh
->installName()) {
590 knownDylibs
.insert({ installName
, &dylib
});
591 allDylibs
.insert({ installName
, &dylib
});
595 for (const CacheBuilder::LoadedMachO
& dylib
: otherDylibs
) {
596 allDylibs
.insert({ dylib
.mappedFile
.runtimePath
, &dylib
});
597 if (const char* installName
= dylib
.mappedFile
.mh
->installName())
598 allDylibs
.insert({ installName
, &dylib
});
601 for (const CacheBuilder::LoadedMachO
& dylib
: couldNotLoadFiles
) {
602 allDylibs
.insert({ dylib
.inputFile
->path
, &dylib
});
605 // Exclude bad unzippered twins. These are where a zippered binary links
606 // an unzippered twin
607 std::unordered_map
<std::string
, std::string
> macOSPathToTwinPath
;
608 for (const CacheBuilder::LoadedMachO
& dylib
: dylibsToCache
) {
609 macOSPathToTwinPath
[dylib
.mappedFile
.runtimePath
] = "";
611 for (const CacheBuilder::LoadedMachO
& dylib
: dylibsToCache
) {
612 if ( startsWith(dylib
.mappedFile
.runtimePath
, "/System/iOSSupport/") ) {
613 std::string tail
= dylib
.mappedFile
.runtimePath
.substr(18);
614 if ( macOSPathToTwinPath
.find(tail
) != macOSPathToTwinPath
.end() )
615 macOSPathToTwinPath
[tail
] = dylib
.mappedFile
.runtimePath
;
619 __block
std::map
<std::string
, std::set
<std::string
>> badDylibs
;
620 for (const CacheBuilder::LoadedMachO
& dylib
: dylibsToCache
) {
621 if ( badDylibs
.count(dylib
.mappedFile
.runtimePath
) != 0 )
623 if ( dylib
.mappedFile
.mh
->isZippered() ) {
624 dylib
.mappedFile
.mh
->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
625 auto macOSAndTwinPath
= macOSPathToTwinPath
.find(loadPath
);
626 if ( macOSAndTwinPath
!= macOSPathToTwinPath
.end() ) {
627 const std::string
& twinPath
= macOSAndTwinPath
->second
;
628 if ( badDylibs
.count(twinPath
) != 0 )
630 knownDylibs
.erase(twinPath
);
631 badDylibs
[twinPath
].insert(std::string("evicting UIKitForMac binary as it is linked by zippered binary '") + dylib
.mappedFile
.runtimePath
+ "'");
637 // HACK: Exclude some dylibs and transitive deps for now until we have project fixes
638 __block
std::set
<std::string
> badProjects
;
639 badProjects
.insert("/System/Library/PrivateFrameworks/TuriCore.framework/Versions/A/TuriCore");
640 badProjects
.insert("/System/Library/PrivateFrameworks/UHASHelloExtensionPoint-macOS.framework/Versions/A/UHASHelloExtensionPoint-macOS");
642 // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
643 __block
bool doAgain
= true;
646 // scan dylib list making sure all dependents are in dylib list
647 for (const CacheBuilder::LoadedMachO
& dylib
: dylibsToCache
) {
648 if ( badDylibs
.count(dylib
.mappedFile
.runtimePath
) != 0 )
650 if ( badProjects
.count(dylib
.mappedFile
.runtimePath
) != 0 )
652 dylib
.mappedFile
.mh
->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
655 if ( badProjects
.count(loadPath
) != 0 ) {
656 // We depend on a bad dylib, so add this one to the list too
657 badProjects
.insert(dylib
.mappedFile
.runtimePath
);
658 badProjects
.insert(dylib
.mappedFile
.mh
->installName());
659 knownDylibs
.erase(dylib
.mappedFile
.runtimePath
);
660 knownDylibs
.erase(dylib
.mappedFile
.mh
->installName());
661 badDylibs
[dylib
.mappedFile
.runtimePath
].insert(std::string("Depends on bad project '") + loadPath
+ "'");
665 char resolvedSymlinkPath
[PATH_MAX
];
666 if ( knownDylibs
.count(loadPath
) == 0 ) {
667 // The loadPath was embedded when the dylib was built, but we may be in the process of moving
668 // a dylib with symlinks from old to new paths
669 // In this case, the realpath will tell us the new location
670 if ( fileSystem
.getRealPath(loadPath
, resolvedSymlinkPath
) ) {
671 if ( strcmp(resolvedSymlinkPath
, loadPath
) != 0 ) {
672 loadPath
= resolvedSymlinkPath
;
676 if ( knownDylibs
.count(loadPath
) == 0 ) {
677 badDylibs
[dylib
.mappedFile
.runtimePath
].insert(std::string("Could not find dependency '") + loadPath
+ "'");
678 knownDylibs
.erase(dylib
.mappedFile
.runtimePath
);
679 knownDylibs
.erase(dylib
.mappedFile
.mh
->installName());
686 // Now walk the dylibs which depend on missing dylibs and see if any of them are required binaries.
687 for (auto badDylibsIterator
: badDylibs
) {
688 const std::string
& dylibRuntimePath
= badDylibsIterator
.first
;
689 auto requiredDylibIterator
= allDylibs
.find(dylibRuntimePath
);
690 if (requiredDylibIterator
== allDylibs
.end())
692 if (!requiredDylibIterator
->second
->inputFile
->mustBeIncluded())
694 // This dylib is required so mark all dependencies as requried too
695 __block
std::vector
<const CacheBuilder::LoadedMachO
*> worklist
;
696 worklist
.push_back(requiredDylibIterator
->second
);
697 while (!worklist
.empty()) {
698 const CacheBuilder::LoadedMachO
* dylib
= worklist
.back();
700 if (!dylib
->mappedFile
.mh
)
702 dylib
->mappedFile
.mh
->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool& stop
) {
705 auto dylibIterator
= allDylibs
.find(loadPath
);
706 if (dylibIterator
!= allDylibs
.end()) {
707 if (dylibIterator
->second
->inputFile
->state
== CacheBuilder::InputFile::Unset
) {
708 dylibIterator
->second
->inputFile
->state
= CacheBuilder::InputFile::MustBeIncludedForDependent
;
709 worklist
.push_back(dylibIterator
->second
);
716 // FIXME: Make this an option we can pass in
717 const bool evictLeafDylibs
= true;
718 if (evictLeafDylibs
) {
723 // build count of how many references there are to each dylib
724 __block
std::set
<std::string
> referencedDylibs
;
725 for (const CacheBuilder::LoadedMachO
& dylib
: dylibsToCache
) {
726 if ( badDylibs
.count(dylib
.mappedFile
.runtimePath
) != 0 )
728 dylib
.mappedFile
.mh
->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
729 referencedDylibs
.insert(loadPath
);
733 // find all dylibs not referenced
734 for (const CacheBuilder::LoadedMachO
& dylib
: dylibsToCache
) {
735 if ( badDylibs
.count(dylib
.mappedFile
.runtimePath
) != 0 )
737 const char* installName
= dylib
.mappedFile
.mh
->installName();
738 if ( (referencedDylibs
.count(installName
) == 0) && (dylib
.inputFile
->state
== CacheBuilder::InputFile::MustBeExcludedIfUnused
) ) {
739 badDylibs
[dylib
.mappedFile
.runtimePath
].insert(std::string("It has been explicitly excluded as it is unused"));
746 // Move bad dylibs from dylibs to cache to other dylibs.
747 for (const CacheBuilder::LoadedMachO
& dylib
: dylibsToCache
) {
748 auto i
= badDylibs
.find(dylib
.mappedFile
.runtimePath
);
749 if ( i
!= badDylibs
.end()) {
750 otherDylibs
.push_back(dylib
);
751 for (const std::string
& reason
: i
->second
)
752 otherDylibs
.back().inputFile
->diag
.warning("Dylib located at '%s' not placed in shared cache because: %s", dylib
.mappedFile
.runtimePath
.c_str(), reason
.c_str());
756 const auto& badDylibsLambdaRef
= badDylibs
;
757 dylibsToCache
.erase(std::remove_if(dylibsToCache
.begin(), dylibsToCache
.end(), [&](const CacheBuilder::LoadedMachO
& dylib
) {
758 if (badDylibsLambdaRef
.find(dylib
.mappedFile
.runtimePath
) != badDylibsLambdaRef
.end())
761 }), dylibsToCache
.end());
764 // This is the new build API which takes the raw files (which could be FAT) and tries to build a cache from them.
765 // We should remove the other build() method, or make it private so that this can wrap it.
766 void SharedCacheBuilder::build(std::vector
<CacheBuilder::InputFile
>& inputFiles
,
767 std::vector
<DyldSharedCache::FileAlias
>& aliases
) {
768 // First filter down to files which are actually MachO's
769 CacheInputBuilder
cacheInputBuilder(_fileSystem
, *_options
.archs
, _options
.platform
);
771 std::vector
<LoadedMachO
> dylibsToCache
;
772 std::vector
<LoadedMachO
> otherDylibs
;
773 std::vector
<LoadedMachO
> executables
;
774 std::vector
<LoadedMachO
> couldNotLoadFiles
;
775 cacheInputBuilder
.loadMachOs(inputFiles
, dylibsToCache
, otherDylibs
, executables
, couldNotLoadFiles
);
777 verifySelfContained(_fileSystem
, dylibsToCache
, otherDylibs
, couldNotLoadFiles
);
779 // Check for required binaries before we try to build the cache
780 if (!_diagnostics
.hasError()) {
781 // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
782 std::string errorString
;
783 for (const LoadedMachO
& dylib
: otherDylibs
) {
784 if (dylib
.inputFile
->mustBeIncluded()) {
785 // An error loading a required file must be propagated up to the top level diagnostic handler.
786 bool gotWarning
= false;
787 for (const std::string
& warning
: dylib
.inputFile
->diag
.warnings()) {
789 std::string message
= warning
;
790 if (message
.back() == '\n')
792 if (!errorString
.empty())
793 errorString
+= "ERROR: ";
794 errorString
+= "Required binary was not included in the shared cache '" + std::string(dylib
.inputFile
->path
) + "' because: " + message
+ "\n";
797 if (!errorString
.empty())
798 errorString
+= "ERROR: ";
799 errorString
+= "Required binary was not included in the shared cache '" + std::string(dylib
.inputFile
->path
) + "' because: 'unknown error. Please report to dyld'\n";
803 for (const LoadedMachO
& dylib
: couldNotLoadFiles
) {
804 if (dylib
.inputFile
->mustBeIncluded()) {
805 if (dylib
.inputFile
->diag
.hasError()) {
806 if (!errorString
.empty())
807 errorString
+= "ERROR: ";
808 errorString
+= "Required binary was not included in the shared cache '" + std::string(dylib
.inputFile
->path
) + "' because: " + dylib
.inputFile
->diag
.errorMessage() + "\n";
810 if (!errorString
.empty())
811 errorString
+= "ERROR: ";
812 errorString
+= "Required binary was not included in the shared cache '" + std::string(dylib
.inputFile
->path
) + "' because: 'unknown error. Please report to dyld'\n";
817 if (!errorString
.empty()) {
818 _diagnostics
.error("%s", errorString
.c_str());
822 if (!_diagnostics
.hasError())
823 build(dylibsToCache
, otherDylibs
, executables
, aliases
);
825 if (!_diagnostics
.hasError()) {
826 // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
827 std::string errorString
;
828 for (CacheBuilder::InputFile
& inputFile
: inputFiles
) {
829 if (inputFile
.mustBeIncluded() && inputFile
.diag
.hasError()) {
830 // An error loading a required file must be propagated up to the top level diagnostic handler.
831 std::string message
= inputFile
.diag
.errorMessage();
832 if (message
.back() == '\n')
834 errorString
+= "Required binary was not included in the shared cache '" + std::string(inputFile
.path
) + "' because: " + message
+ "\n";
837 if (!errorString
.empty()) {
838 _diagnostics
.error("%s", errorString
.c_str());
842 // Add all the warnings from the input files to the top level warnings on the main diagnostics object.
843 for (CacheBuilder::InputFile
& inputFile
: inputFiles
) {
844 for (const std::string
& warning
: inputFile
.diag
.warnings())
845 _diagnostics
.warning("%s", warning
.c_str());
848 // Clean up the loaded files
849 for (LoadedMachO
& loadedMachO
: dylibsToCache
)
850 _fileSystem
.unloadFile(loadedMachO
.loadedFileInfo
);
851 for (LoadedMachO
& loadedMachO
: otherDylibs
)
852 _fileSystem
.unloadFile(loadedMachO
.loadedFileInfo
);
853 for (LoadedMachO
& loadedMachO
: executables
)
854 _fileSystem
.unloadFile(loadedMachO
.loadedFileInfo
);
857 void SharedCacheBuilder::build(const std::vector
<DyldSharedCache::MappedMachO
>& dylibs
,
858 const std::vector
<DyldSharedCache::MappedMachO
>& otherOsDylibsInput
,
859 const std::vector
<DyldSharedCache::MappedMachO
>& osExecutables
,
860 std::vector
<DyldSharedCache::FileAlias
>& aliases
) {
862 std::vector
<LoadedMachO
> dylibsToCache
;
863 std::vector
<LoadedMachO
> otherDylibs
;
864 std::vector
<LoadedMachO
> executables
;
866 for (const DyldSharedCache::MappedMachO
& mappedMachO
: dylibs
) {
867 dyld3::closure::LoadedFileInfo loadedFileInfo
;
868 loadedFileInfo
.fileContent
= mappedMachO
.mh
;
869 loadedFileInfo
.fileContentLen
= mappedMachO
.length
;
870 loadedFileInfo
.sliceOffset
= mappedMachO
.sliceFileOffset
;
871 loadedFileInfo
.sliceLen
= mappedMachO
.length
;
872 loadedFileInfo
.inode
= mappedMachO
.inode
;
873 loadedFileInfo
.mtime
= mappedMachO
.modTime
;
874 loadedFileInfo
.path
= mappedMachO
.runtimePath
.c_str();
875 dylibsToCache
.emplace_back((LoadedMachO
){ mappedMachO
, loadedFileInfo
, nullptr });
878 for (const DyldSharedCache::MappedMachO
& mappedMachO
: otherOsDylibsInput
) {
879 dyld3::closure::LoadedFileInfo loadedFileInfo
;
880 loadedFileInfo
.fileContent
= mappedMachO
.mh
;
881 loadedFileInfo
.fileContentLen
= mappedMachO
.length
;
882 loadedFileInfo
.sliceOffset
= mappedMachO
.sliceFileOffset
;
883 loadedFileInfo
.sliceLen
= mappedMachO
.length
;
884 loadedFileInfo
.inode
= mappedMachO
.inode
;
885 loadedFileInfo
.mtime
= mappedMachO
.modTime
;
886 loadedFileInfo
.path
= mappedMachO
.runtimePath
.c_str();
887 otherDylibs
.emplace_back((LoadedMachO
){ mappedMachO
, loadedFileInfo
, nullptr });
890 for (const DyldSharedCache::MappedMachO
& mappedMachO
: osExecutables
) {
891 dyld3::closure::LoadedFileInfo loadedFileInfo
;
892 loadedFileInfo
.fileContent
= mappedMachO
.mh
;
893 loadedFileInfo
.fileContentLen
= mappedMachO
.length
;
894 loadedFileInfo
.sliceOffset
= mappedMachO
.sliceFileOffset
;
895 loadedFileInfo
.sliceLen
= mappedMachO
.length
;
896 loadedFileInfo
.inode
= mappedMachO
.inode
;
897 loadedFileInfo
.mtime
= mappedMachO
.modTime
;
898 loadedFileInfo
.path
= mappedMachO
.runtimePath
.c_str();
899 executables
.emplace_back((LoadedMachO
){ mappedMachO
, loadedFileInfo
, nullptr });
902 build(dylibsToCache
, otherDylibs
, executables
, aliases
);
905 void SharedCacheBuilder::build(const std::vector
<LoadedMachO
>& dylibs
,
906 const std::vector
<LoadedMachO
>& otherOsDylibsInput
,
907 const std::vector
<LoadedMachO
>& osExecutables
,
908 std::vector
<DyldSharedCache::FileAlias
>& aliases
)
910 // <rdar://problem/21317611> error out instead of crash if cache has no dylibs
911 // FIXME: plist should specify required vs optional dylibs
912 if ( dylibs
.size() < 30 ) {
913 _diagnostics
.error("missing required minimum set of dylibs");
917 _timeRecorder
.pushTimedSection();
919 // make copy of dylib list and sort
920 makeSortedDylibs(dylibs
, _options
.dylibOrdering
);
922 // allocate space used by largest possible cache plus room for LINKEDITS before optimization
923 _allocatedBufferSize
= _archLayout
->sharedMemorySize
* 1.50;
924 if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer
, _allocatedBufferSize
, VM_FLAGS_ANYWHERE
) != 0 ) {
925 _diagnostics
.error("could not allocate buffer");
929 _timeRecorder
.recordTime("sort dylibs");
931 bool impCachesSuccess
= false;
932 IMPCaches::HoleMap selectorAddressIntervals
;
933 _impCachesBuilder
= new IMPCaches::IMPCachesBuilder(_sortedDylibs
, _options
.objcOptimizations
, _diagnostics
, _timeRecorder
, _fileSystem
);
935 // Note, macOS allows install names and paths to mismatch. This is currently not supported by
936 // IMP caches as we use install names to look up the set of dylibs.
937 if ( _archLayout
->is64
938 && (_options
.platform
!= dyld3::Platform::macOS
)
939 && ((_impCachesBuilder
->neededClasses
.size() > 0) || (_impCachesBuilder
->neededMetaclasses
.size() > 0))) {
940 // Build the class map across all dylibs (including cross-image superclass references)
941 _impCachesBuilder
->buildClassesMap(_diagnostics
);
943 // Determine which methods will end up in each class's IMP cache
944 impCachesSuccess
= _impCachesBuilder
->parseDylibs(_diagnostics
);
946 // Compute perfect hash functions for IMP caches
947 if (impCachesSuccess
) _impCachesBuilder
->buildPerfectHashes(selectorAddressIntervals
, _diagnostics
);
950 constexpr bool log
= false;
952 for (const auto& p
: _impCachesBuilder
->selectors
.map
) {
953 printf("0x%06x %s\n", p
.second
->offset
, p
.second
->name
);
957 _timeRecorder
.recordTime("compute IMP caches");
959 IMPCaches::SelectorMap emptyMap
;
960 IMPCaches::SelectorMap
& selectorMap
= impCachesSuccess
? _impCachesBuilder
->selectors
: emptyMap
;
961 // assign addresses for each segment of each dylib in new cache
962 parseCoalescableSegments(selectorMap
, selectorAddressIntervals
);
963 processSelectorStrings(osExecutables
, selectorAddressIntervals
);
965 assignSegmentAddresses();
966 std::vector
<const LoadedMachO
*> overflowDylibs
;
967 while ( cacheOverflowAmount() != 0 ) {
968 // IMP caches: we may need to recompute the selector addresses here to be slightly more compact
969 // if we remove dylibs? This is probably overkill.
971 if ( !_options
.evictLeafDylibsOnOverflow
) {
972 _diagnostics
.error("cache overflow by %lluMB", cacheOverflowAmount() / 1024 / 1024);
975 size_t evictionCount
= evictLeafDylibs(cacheOverflowAmount(), overflowDylibs
);
977 for (DylibInfo
& dylib
: _sortedDylibs
)
978 dylib
.cacheLocation
.clear();
979 _dataRegions
.clear();
980 _coalescedText
.clear();
982 // Re-generate the hole map to remove any cruft that was added when parsing the coalescable text the first time.
983 // Always clear the hole map, even if IMP caches are off, as it is used by the text coalescer
984 selectorAddressIntervals
.clear();
985 if (impCachesSuccess
) _impCachesBuilder
->computeLowBits(selectorAddressIntervals
);
987 parseCoalescableSegments(selectorMap
, selectorAddressIntervals
);
988 processSelectorStrings(osExecutables
, selectorAddressIntervals
);
989 assignSegmentAddresses();
991 _diagnostics
.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount
);
993 markPaddingInaccessible();
995 // copy all segments into cache
997 unsigned long wastedSelectorsSpace
= selectorAddressIntervals
.totalHoleSize();
998 if (wastedSelectorsSpace
> 0) {
999 _diagnostics
.verbose("Selector placement for IMP caches wasted %lu bytes\n", wastedSelectorsSpace
);
1001 std::cerr
<< selectorAddressIntervals
<< std::endl
;
1005 _timeRecorder
.recordTime("layout cache");
1009 _timeRecorder
.recordTime("copy cached dylibs into buffer");
1011 // rebase all dylibs for new location in cache
1013 _aslrTracker
.setDataRegion(firstDataRegion()->buffer
, dataRegionsTotalSize());
1014 if ( !_options
.cacheSupportsASLR
)
1015 _aslrTracker
.disable();
1016 adjustAllImagesForNewSegmentLocations(_archLayout
->sharedMemoryStart
, _aslrTracker
,
1017 &_lohTracker
, &_coalescedText
);
1018 if ( _diagnostics
.hasError() )
1021 _timeRecorder
.recordTime("adjust segments for new split locations");
1023 // build ImageArray for dyld3, which has side effect of binding all cached dylibs
1024 buildImageArray(aliases
);
1025 if ( _diagnostics
.hasError() )
1028 _timeRecorder
.recordTime("bind all images");
1031 DyldSharedCache
* dyldCache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
1032 optimizeObjC(impCachesSuccess
, _impCachesBuilder
->inlinedSelectors
);
1034 delete _impCachesBuilder
;
1035 _impCachesBuilder
= nullptr;
1037 if ( _diagnostics
.hasError() )
1040 _timeRecorder
.recordTime("optimize Objective-C");
1042 if ( _options
.optimizeStubs
) {
1043 __block
std::vector
<std::pair
<const mach_header
*, const char*>> images
;
1044 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
1045 images
.push_back({ mh
, installName
});
1048 int64_t cacheSlide
= (long)dyldCache
- dyldCache
->unslidLoadAddress();
1049 uint64_t cacheUnslideAddr
= dyldCache
->unslidLoadAddress();
1050 optimizeAwayStubs(images
, cacheSlide
, cacheUnslideAddr
,
1051 dyldCache
, _s_neverStubEliminateSymbols
);
1055 // FIPS seal corecrypto, This must be done after stub elimination (so that __TEXT,__text is not changed after sealing)
1058 _timeRecorder
.recordTime("do stub elimination");
1060 // merge and compact LINKEDIT segments
1062 // If we want to remove, not just unmap locals, then set the dylibs themselves to be stripped
1063 DylibStripMode dylibStripMode
= DylibStripMode::stripNone
;
1064 if ( _options
.localSymbolMode
== DyldSharedCache::LocalSymbolsMode::strip
)
1065 dylibStripMode
= CacheBuilder::DylibStripMode::stripLocals
;
1067 __block
std::vector
<std::tuple
<const mach_header
*, const char*, DylibStripMode
>> images
;
1068 dyldCache
->forEachImage(^(const mach_header
*mh
, const char *installName
) {
1069 images
.push_back({ mh
, installName
, dylibStripMode
});
1071 optimizeLinkedit(&_localSymbolsRegion
, images
);
1074 // copy ImageArray to end of read-only region
1076 if ( _diagnostics
.hasError() )
1079 _timeRecorder
.recordTime("optimize LINKEDITs");
1081 // don't add dyld3 closures to simulator cache or the base system where size is more of an issue
1082 if ( _options
.optimizeDyldDlopens
) {
1083 // compute and add dlopen closures for all other dylibs
1084 addOtherImageArray(otherOsDylibsInput
, overflowDylibs
);
1085 if ( _diagnostics
.hasError() )
1088 if ( _options
.optimizeDyldLaunches
) {
1089 // compute and add launch closures to end of read-only region
1090 addClosures(osExecutables
);
1091 if ( _diagnostics
.hasError() )
1095 // update final readOnly region size
1096 dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)(_readExecuteRegion
.buffer
+ dyldCache
->header
.mappingOffset
);
1097 mappings
[dyldCache
->header
.mappingCount
- 1].size
= _readOnlyRegion
.sizeInUse
;
1098 dyld_cache_mapping_and_slide_info
* slidableMappings
= (dyld_cache_mapping_and_slide_info
*)(_readExecuteRegion
.buffer
+ dyldCache
->header
.mappingWithSlideOffset
);
1099 slidableMappings
[dyldCache
->header
.mappingCount
- 1].size
= _readOnlyRegion
.sizeInUse
;
1100 if ( _localSymbolsRegion
.sizeInUse
!= 0 ) {
1101 dyldCache
->header
.localSymbolsOffset
= _readOnlyRegion
.cacheFileOffset
+ _readOnlyRegion
.sizeInUse
;
1102 dyldCache
->header
.localSymbolsSize
= _localSymbolsRegion
.sizeInUse
;
1105 // record max slide now that final size is established
1106 if ( _archLayout
->sharedRegionsAreDiscontiguous
) {
1107 // special case x86_64 which has three non-contiguous chunks each in their own 1GB regions
1108 uint64_t maxSlide0
= DISCONTIGUOUS_RX_SIZE
- _readExecuteRegion
.sizeInUse
; // TEXT region has 1.5GB region
1109 uint64_t maxSlide1
= DISCONTIGUOUS_RW_SIZE
- dataRegionsTotalSize();
1110 uint64_t maxSlide2
= DISCONTIGUOUS_RO_SIZE
- _readOnlyRegion
.sizeInUse
;
1111 dyldCache
->header
.maxSlide
= std::min(std::min(maxSlide0
, maxSlide1
), maxSlide2
);
1114 // <rdar://problem/49852839> branch predictor on arm64 currently only looks at low 32-bits, so don't slide cache more than 2GB
1115 if ( (_archLayout
->sharedMemorySize
== 0x100000000) && (_readExecuteRegion
.sizeInUse
< 0x80000000) )
1116 dyldCache
->header
.maxSlide
= 0x80000000 - _readExecuteRegion
.sizeInUse
;
1118 dyldCache
->header
.maxSlide
= (_archLayout
->sharedMemoryStart
+ _archLayout
->sharedMemorySize
) - (_readOnlyRegion
.unslidLoadAddress
+ _readOnlyRegion
.sizeInUse
);
1121 // mark if any input dylibs were built with chained fixups
1122 dyldCache
->header
.builtFromChainedFixups
= _someDylibsUsedChainedFixups
;
1124 _timeRecorder
.recordTime("build %lu closures", osExecutables
.size());
1125 // Emit the CF strings without their ISAs being signed
1126 // This must be after addImageArray() as it depends on hasImageIndex().
1127 // It also has to be before emitting slide info as it adds ASLR entries.
1128 emitContantObjects();
1130 _timeRecorder
.recordTime("emit constant objects");
1132 // fill in slide info at start of region[2]
1133 // do this last because it modifies pointers in DATA segments
1134 if ( _options
.cacheSupportsASLR
) {
1135 #if SUPPORT_ARCH_arm64e
1136 if ( strcmp(_archLayout
->archName
, "arm64e") == 0 )
1137 writeSlideInfoV3(_aslrTracker
.bitmap(), _aslrTracker
.dataPageCount());
1140 if ( _archLayout
->is64
)
1141 writeSlideInfoV2
<Pointer64
<LittleEndian
>>(_aslrTracker
.bitmap(), _aslrTracker
.dataPageCount());
1142 #if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
1143 else if ( _archLayout
->pointerDeltaMask
== 0xC0000000 )
1144 writeSlideInfoV4
<Pointer32
<LittleEndian
>>(_aslrTracker
.bitmap(), _aslrTracker
.dataPageCount());
1147 writeSlideInfoV2
<Pointer32
<LittleEndian
>>(_aslrTracker
.bitmap(), _aslrTracker
.dataPageCount());
1150 _timeRecorder
.recordTime("compute slide info");
1152 // last sanity check on size
1153 if ( cacheOverflowAmount() != 0 ) {
1154 _diagnostics
.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion
.unslidLoadAddress
, _readOnlyRegion
.unslidLoadAddress
+ _readOnlyRegion
.sizeInUse
);
1158 // codesignature is part of file, but is not mapped
1160 if ( _diagnostics
.hasError() )
1163 _timeRecorder
.recordTime("compute UUID and codesign cache file");
1165 if (_options
.verbose
) {
1166 _timeRecorder
.logTimings();
1172 const std::set
<std::string
> SharedCacheBuilder::warnings()
1174 return _diagnostics
.warnings();
1177 const std::set
<const dyld3::MachOAnalyzer
*> SharedCacheBuilder::evictions()
1182 void SharedCacheBuilder::deleteBuffer()
1185 if ( _allocatedBufferSize
!= 0 ) {
1186 vm_deallocate(mach_task_self(), _fullAllocatedBuffer
, _allocatedBufferSize
);
1187 _fullAllocatedBuffer
= 0;
1188 _allocatedBufferSize
= 0;
1190 // Local symbols buffer
1191 if ( _localSymbolsRegion
.bufferSize
!= 0 ) {
1192 vm_deallocate(mach_task_self(), (vm_address_t
)_localSymbolsRegion
.buffer
, _localSymbolsRegion
.bufferSize
);
1193 _localSymbolsRegion
.buffer
= 0;
1194 _localSymbolsRegion
.bufferSize
= 0;
1197 if ( _codeSignatureRegion
.bufferSize
!= 0 ) {
1198 vm_deallocate(mach_task_self(), (vm_address_t
)_codeSignatureRegion
.buffer
, _codeSignatureRegion
.bufferSize
);
1199 _codeSignatureRegion
.buffer
= 0;
1200 _codeSignatureRegion
.bufferSize
= 0;
1205 void SharedCacheBuilder::makeSortedDylibs(const std::vector
<LoadedMachO
>& dylibs
, const std::unordered_map
<std::string
, unsigned> sortOrder
)
1207 for (const LoadedMachO
& dylib
: dylibs
) {
1208 _sortedDylibs
.push_back({ &dylib
, dylib
.mappedFile
.runtimePath
, {} });
1211 std::sort(_sortedDylibs
.begin(), _sortedDylibs
.end(), [&](const DylibInfo
& a
, const DylibInfo
& b
) {
1212 const auto& orderA
= sortOrder
.find(a
.input
->mappedFile
.runtimePath
);
1213 const auto& orderB
= sortOrder
.find(b
.input
->mappedFile
.runtimePath
);
1214 bool foundA
= (orderA
!= sortOrder
.end());
1215 bool foundB
= (orderB
!= sortOrder
.end());
1217 // Order all __DATA_DIRTY segments specified in the order file first, in
1218 // the order specified in the file, followed by any other __DATA_DIRTY
1219 // segments in lexicographic order.
1220 if ( foundA
&& foundB
)
1221 return orderA
->second
< orderB
->second
;
1227 // Sort mac before iOSMac
1228 bool isIOSMacA
= strncmp(a
.input
->mappedFile
.runtimePath
.c_str(), "/System/iOSSupport/", 19) == 0;
1229 bool isIOSMacB
= strncmp(b
.input
->mappedFile
.runtimePath
.c_str(), "/System/iOSSupport/", 19) == 0;
1230 if (isIOSMacA
!= isIOSMacB
)
1233 // Finally sort by path
1234 return a
.input
->mappedFile
.runtimePath
< b
.input
->mappedFile
.runtimePath
;
1240 const CacheBuilder::LoadedMachO
* input
;
1241 const char* installName
;
1245 uint64_t SharedCacheBuilder::cacheOverflowAmount()
1247 if ( _archLayout
->sharedRegionsAreDiscontiguous
) {
1248 // for macOS x86_64 cache, need to check each region for overflow
1249 if ( _readExecuteRegion
.sizeInUse
> DISCONTIGUOUS_RX_SIZE
)
1250 return (_readExecuteRegion
.sizeInUse
- DISCONTIGUOUS_RX_SIZE
);
1252 uint64_t dataSize
= dataRegionsTotalSize();
1253 if ( dataSize
> DISCONTIGUOUS_RW_SIZE
)
1254 return (dataSize
- DISCONTIGUOUS_RW_SIZE
);
1256 if ( _readOnlyRegion
.sizeInUse
> DISCONTIGUOUS_RO_SIZE
)
1257 return (_readOnlyRegion
.sizeInUse
- DISCONTIGUOUS_RO_SIZE
);
1260 bool alreadyOptimized
= (_readOnlyRegion
.sizeInUse
!= _readOnlyRegion
.bufferSize
);
1261 uint64_t vmSize
= _readOnlyRegion
.unslidLoadAddress
- _readExecuteRegion
.unslidLoadAddress
;
1262 if ( alreadyOptimized
)
1263 vmSize
+= _readOnlyRegion
.sizeInUse
;
1264 else if ( _options
.localSymbolMode
== DyldSharedCache::LocalSymbolsMode::unmap
)
1265 vmSize
+= (_readOnlyRegion
.sizeInUse
* 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %37 of original size
1267 vmSize
+= (_readOnlyRegion
.sizeInUse
* 80/100); // assume LINKEDIT optimzation reduces LINKEDITs to %80 of original size
1268 if ( vmSize
> _archLayout
->sharedMemorySize
)
1269 return vmSize
- _archLayout
->sharedMemorySize
;
1271 // fits in shared region
1275 size_t SharedCacheBuilder::evictLeafDylibs(uint64_t reductionTarget
, std::vector
<const LoadedMachO
*>& overflowDylibs
)
1277 // build a reverse map of all dylib dependencies
1278 __block
std::map
<std::string
, std::set
<std::string
>> references
;
1279 std::map
<std::string
, std::set
<std::string
>>* referencesPtr
= &references
;
1280 for (const DylibInfo
& dylib
: _sortedDylibs
) {
1281 // Esnure we have an entry (even if it is empty)
1282 if (references
.count(dylib
.input
->mappedFile
.mh
->installName()) == 0) {
1283 references
[dylib
.input
->mappedFile
.mh
->installName()] = std::set
<std::string
>();
1285 dylib
.input
->mappedFile
.mh
->forEachDependentDylib(^(const char* loadPath
, bool isWeak
, bool isReExport
, bool isUpward
, uint32_t compatVersion
, uint32_t curVersion
, bool &stop
) {
1286 references
[loadPath
].insert(dylib
.input
->mappedFile
.mh
->installName());
1290 // Find the sizes of all the dylibs
1291 std::vector
<DylibAndSize
> dylibsToSort
;
1292 std::vector
<DylibAndSize
> sortedDylibs
;
1293 for (const DylibInfo
& dylib
: _sortedDylibs
) {
1294 const char* installName
= dylib
.input
->mappedFile
.mh
->installName();
1295 __block
uint64_t segsSize
= 0;
1296 dylib
.input
->mappedFile
.mh
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& info
, bool& stop
) {
1297 if ( strcmp(info
.segName
, "__LINKEDIT") != 0 )
1298 segsSize
+= info
.vmSize
;
1300 dylibsToSort
.push_back({ dylib
.input
, installName
, segsSize
});
1303 // Build an ordered list of what to remove. At each step we do following
1304 // 1) Find all dylibs that nothing else depends on
1305 // 2a) If any of those dylibs are not in the order select the largest one of them
1306 // 2b) If all the leaf dylibs are in the order file select the last dylib that appears last in the order file
1307 // 3) Remove all entries to the removed file from the reverse dependency map
1308 // 4) Go back to one and repeat until there are no more evictable dylibs
1309 // This results in us always choosing the locally optimal selection, and then taking into account how that impacts
1310 // the dependency graph for subsequent selections
1312 bool candidateFound
= true;
1313 while (candidateFound
) {
1314 candidateFound
= false;
1315 DylibAndSize candidate
;
1316 uint64_t candidateOrder
= 0;
1317 for(const auto& dylib
: dylibsToSort
) {
1318 const auto& i
= referencesPtr
->find(dylib
.installName
);
1319 assert(i
!= referencesPtr
->end());
1320 if (!i
->second
.empty()) {
1323 const auto& j
= _options
.dylibOrdering
.find(dylib
.input
->mappedFile
.runtimePath
);
1325 if (j
!= _options
.dylibOrdering
.end()) {
1328 // Not in the order file, set order sot it goes to the front of the list
1331 if (order
> candidateOrder
||
1332 (order
== UINT64_MAX
&& candidate
.size
< dylib
.size
)) {
1333 // The new file is either a lower priority in the order file
1334 // or the same priority as the candidate but larger
1336 candidateOrder
= order
;
1337 candidateFound
= true;
1340 if (candidateFound
) {
1341 sortedDylibs
.push_back(candidate
);
1342 referencesPtr
->erase(candidate
.installName
);
1343 for (auto& dependent
: references
) {
1344 (void)dependent
.second
.erase(candidate
.installName
);
1346 auto j
= std::find_if(dylibsToSort
.begin(), dylibsToSort
.end(), [&candidate
](const DylibAndSize
& dylib
) {
1347 return (strcmp(candidate
.installName
, dylib
.installName
) == 0);
1349 if (j
!= dylibsToSort
.end()) {
1350 dylibsToSort
.erase(j
);
1355 // build set of dylibs that if removed will allow cache to build
1356 for (DylibAndSize
& dylib
: sortedDylibs
) {
1357 if ( _options
.verbose
)
1358 _diagnostics
.warning("to prevent cache overflow, not caching %s", dylib
.installName
);
1359 _evictions
.insert(dylib
.input
->mappedFile
.mh
);
1360 // Track the evicted dylibs so we can try build "other" dlopen closures for them.
1361 overflowDylibs
.push_back(dylib
.input
);
1362 if ( dylib
.size
> reductionTarget
)
1364 reductionTarget
-= dylib
.size
;
1367 // prune _sortedDylibs
1368 _sortedDylibs
.erase(std::remove_if(_sortedDylibs
.begin(), _sortedDylibs
.end(), [&](const DylibInfo
& dylib
) {
1369 return (_evictions
.count(dylib
.input
->mappedFile
.mh
) != 0);
1370 }),_sortedDylibs
.end());
1372 return _evictions
.size();
1376 void SharedCacheBuilder::writeCacheHeader()
1378 // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
1379 std::string magic
= "dyld_v1";
1380 magic
.append(15 - magic
.length() - strlen(_options
.archs
->name()), ' ');
1381 magic
.append(_options
.archs
->name());
1382 assert(magic
.length() == 15);
1384 // 1 __TEXT segment, n __DATA segments, and 1 __LINKEDIT segment
1385 const uint32_t mappingCount
= 2 + (uint32_t)_dataRegions
.size();
1386 assert(mappingCount
<= DyldSharedCache::MaxMappings
);
1389 dyld_cache_header
* dyldCacheHeader
= (dyld_cache_header
*)_readExecuteRegion
.buffer
;
1390 memcpy(dyldCacheHeader
->magic
, magic
.c_str(), 16);
1391 dyldCacheHeader
->mappingOffset
= sizeof(dyld_cache_header
);
1392 dyldCacheHeader
->mappingCount
= mappingCount
;
1393 dyldCacheHeader
->mappingWithSlideOffset
= (uint32_t)(dyldCacheHeader
->mappingOffset
+ mappingCount
*sizeof(dyld_cache_mapping_and_slide_info
));
1394 dyldCacheHeader
->mappingWithSlideCount
= mappingCount
;
1395 dyldCacheHeader
->imagesOffset
= (uint32_t)(dyldCacheHeader
->mappingWithSlideOffset
+ mappingCount
*sizeof(dyld_cache_mapping_and_slide_info
));
1396 dyldCacheHeader
->imagesCount
= (uint32_t)_sortedDylibs
.size() + _aliasCount
;
1397 dyldCacheHeader
->dyldBaseAddress
= 0;
1398 dyldCacheHeader
->codeSignatureOffset
= 0;
1399 dyldCacheHeader
->codeSignatureSize
= 0;
1400 dyldCacheHeader
->slideInfoOffsetUnused
= 0;
1401 dyldCacheHeader
->slideInfoSizeUnused
= 0;
1402 dyldCacheHeader
->localSymbolsOffset
= 0;
1403 dyldCacheHeader
->localSymbolsSize
= 0;
1404 dyldCacheHeader
->cacheType
= _options
.optimizeStubs
? kDyldSharedCacheTypeProduction
: kDyldSharedCacheTypeDevelopment
;
1405 dyldCacheHeader
->accelerateInfoAddr
= 0;
1406 dyldCacheHeader
->accelerateInfoSize
= 0;
1407 bzero(dyldCacheHeader
->uuid
, 16);// overwritten later by recomputeCacheUUID()
1408 dyldCacheHeader
->branchPoolsOffset
= 0;
1409 dyldCacheHeader
->branchPoolsCount
= 0;
1410 dyldCacheHeader
->imagesTextOffset
= dyldCacheHeader
->imagesOffset
+ sizeof(dyld_cache_image_info
)*dyldCacheHeader
->imagesCount
;
1411 dyldCacheHeader
->imagesTextCount
= _sortedDylibs
.size();
1412 dyldCacheHeader
->patchInfoAddr
= 0;
1413 dyldCacheHeader
->patchInfoSize
= 0;
1414 dyldCacheHeader
->otherImageGroupAddrUnused
= 0;
1415 dyldCacheHeader
->otherImageGroupSizeUnused
= 0;
1416 dyldCacheHeader
->progClosuresAddr
= 0;
1417 dyldCacheHeader
->progClosuresSize
= 0;
1418 dyldCacheHeader
->progClosuresTrieAddr
= 0;
1419 dyldCacheHeader
->progClosuresTrieSize
= 0;
1420 dyldCacheHeader
->platform
= (uint8_t)_options
.platform
;
1421 dyldCacheHeader
->formatVersion
= dyld3::closure::kFormatVersion
;
1422 dyldCacheHeader
->dylibsExpectedOnDisk
= !_options
.dylibsRemovedDuringMastering
;
1423 dyldCacheHeader
->simulator
= _options
.forSimulator
;
1424 dyldCacheHeader
->locallyBuiltCache
= _options
.isLocallyBuiltCache
;
1425 dyldCacheHeader
->builtFromChainedFixups
= false;
1426 dyldCacheHeader
->formatVersion
= dyld3::closure::kFormatVersion
;
1427 dyldCacheHeader
->sharedRegionStart
= _archLayout
->sharedMemoryStart
;
1428 dyldCacheHeader
->sharedRegionSize
= _archLayout
->sharedMemorySize
;
1431 dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)(_readExecuteRegion
.buffer
+ dyldCacheHeader
->mappingOffset
);
1432 assert(_readExecuteRegion
.cacheFileOffset
== 0);
1433 mappings
[0].address
= _readExecuteRegion
.unslidLoadAddress
;
1434 mappings
[0].fileOffset
= _readExecuteRegion
.cacheFileOffset
;
1435 mappings
[0].size
= _readExecuteRegion
.sizeInUse
;
1436 mappings
[0].maxProt
= VM_PROT_READ
| VM_PROT_EXECUTE
;
1437 mappings
[0].initProt
= VM_PROT_READ
| VM_PROT_EXECUTE
;
1438 for (uint32_t i
= 0; i
!= _dataRegions
.size(); ++i
) {
1440 assert(_dataRegions
[i
].cacheFileOffset
== _readExecuteRegion
.sizeInUse
);
1443 assert(_dataRegions
[i
].initProt
!= 0);
1444 assert(_dataRegions
[i
].maxProt
!= 0);
1446 mappings
[i
+ 1].address
= _dataRegions
[i
].unslidLoadAddress
;
1447 mappings
[i
+ 1].fileOffset
= _dataRegions
[i
].cacheFileOffset
;
1448 mappings
[i
+ 1].size
= _dataRegions
[i
].sizeInUse
;
1449 mappings
[i
+ 1].maxProt
= _dataRegions
[i
].maxProt
;
1450 mappings
[i
+ 1].initProt
= _dataRegions
[i
].initProt
;
1452 assert(_readOnlyRegion
.cacheFileOffset
== (_dataRegions
.back().cacheFileOffset
+ _dataRegions
.back().sizeInUse
));
1453 mappings
[mappingCount
- 1].address
= _readOnlyRegion
.unslidLoadAddress
;
1454 mappings
[mappingCount
- 1].fileOffset
= _readOnlyRegion
.cacheFileOffset
;
1455 mappings
[mappingCount
- 1].size
= _readOnlyRegion
.sizeInUse
;
1456 mappings
[mappingCount
- 1].maxProt
= VM_PROT_READ
;
1457 mappings
[mappingCount
- 1].initProt
= VM_PROT_READ
;
1459 // Add in the new mappings with also have slide info
1460 dyld_cache_mapping_and_slide_info
* slidableMappings
= (dyld_cache_mapping_and_slide_info
*)(_readExecuteRegion
.buffer
+ dyldCacheHeader
->mappingWithSlideOffset
);
1461 slidableMappings
[0].address
= _readExecuteRegion
.unslidLoadAddress
;
1462 slidableMappings
[0].fileOffset
= _readExecuteRegion
.cacheFileOffset
;
1463 slidableMappings
[0].size
= _readExecuteRegion
.sizeInUse
;
1464 slidableMappings
[0].maxProt
= VM_PROT_READ
| VM_PROT_EXECUTE
;
1465 slidableMappings
[0].initProt
= VM_PROT_READ
| VM_PROT_EXECUTE
;
1466 slidableMappings
[0].slideInfoFileOffset
= 0;
1467 slidableMappings
[0].slideInfoFileSize
= 0;
1468 slidableMappings
[0].flags
= 0;
1469 for (uint32_t i
= 0; i
!= _dataRegions
.size(); ++i
) {
1470 // Work out which flags this mapping has
1472 if ( startsWith(_dataRegions
[i
].name
, "__AUTH") )
1473 flags
|= DYLD_CACHE_MAPPING_AUTH_DATA
;
1474 if ( (_dataRegions
[i
].name
== "__AUTH_DIRTY") || (_dataRegions
[i
].name
== "__DATA_DIRTY") ) {
1475 flags
|= DYLD_CACHE_MAPPING_DIRTY_DATA
;
1476 } else if ( (_dataRegions
[i
].name
== "__AUTH_CONST") || (_dataRegions
[i
].name
== "__DATA_CONST") ) {
1477 flags
|= DYLD_CACHE_MAPPING_CONST_DATA
;
1480 assert(_dataRegions
[i
].initProt
!= 0);
1481 assert(_dataRegions
[i
].maxProt
!= 0);
1483 slidableMappings
[i
+ 1].address
= _dataRegions
[i
].unslidLoadAddress
;
1484 slidableMappings
[i
+ 1].fileOffset
= _dataRegions
[i
].cacheFileOffset
;
1485 slidableMappings
[i
+ 1].size
= _dataRegions
[i
].sizeInUse
;
1486 slidableMappings
[i
+ 1].maxProt
= _dataRegions
[i
].maxProt
;
1487 slidableMappings
[i
+ 1].initProt
= _dataRegions
[i
].initProt
;
1488 slidableMappings
[i
+ 1].slideInfoFileOffset
= _dataRegions
[i
].slideInfoFileOffset
;
1489 slidableMappings
[i
+ 1].slideInfoFileSize
= _dataRegions
[i
].slideInfoFileSize
;
1490 slidableMappings
[i
+ 1].flags
= flags
;
1492 slidableMappings
[mappingCount
- 1].address
= _readOnlyRegion
.unslidLoadAddress
;
1493 slidableMappings
[mappingCount
- 1].fileOffset
= _readOnlyRegion
.cacheFileOffset
;
1494 slidableMappings
[mappingCount
- 1].size
= _readOnlyRegion
.sizeInUse
;
1495 slidableMappings
[mappingCount
- 1].maxProt
= VM_PROT_READ
;
1496 slidableMappings
[mappingCount
- 1].initProt
= VM_PROT_READ
;
1497 slidableMappings
[mappingCount
- 1].slideInfoFileOffset
= 0;
1498 slidableMappings
[mappingCount
- 1].slideInfoFileSize
= 0;
1499 slidableMappings
[mappingCount
- 1].flags
= 0;
1501 // fill in image table
1502 dyld_cache_image_info
* images
= (dyld_cache_image_info
*)(_readExecuteRegion
.buffer
+ dyldCacheHeader
->imagesOffset
);
1503 for (const DylibInfo
& dylib
: _sortedDylibs
) {
1504 const char* installName
= dylib
.input
->mappedFile
.mh
->installName();
1505 images
->address
= dylib
.cacheLocation
[0].dstCacheUnslidAddress
;
1506 if ( _options
.dylibsRemovedDuringMastering
) {
1507 images
->modTime
= 0;
1508 images
->inode
= pathHash(installName
);
1511 images
->modTime
= dylib
.input
->mappedFile
.modTime
;
1512 images
->inode
= dylib
.input
->mappedFile
.inode
;
1514 uint32_t installNameOffsetInTEXT
= (uint32_t)(installName
- (char*)dylib
.input
->mappedFile
.mh
);
1515 images
->pathFileOffset
= (uint32_t)dylib
.cacheLocation
[0].dstCacheFileOffset
+ installNameOffsetInTEXT
;
1518 // append aliases image records and strings
1520 for (auto &dylib : _dylibs) {
1521 if (!dylib->installNameAliases.empty()) {
1522 for (const std::string& alias : dylib->installNameAliases) {
1523 images->set_address(_segmentMap[dylib][0].address);
1524 if (_manifest.platform() == "osx") {
1525 images->modTime = dylib->lastModTime;
1526 images->inode = dylib->inode;
1529 images->modTime = 0;
1530 images->inode = pathHash(alias.c_str());
1532 images->pathFileOffset = offset;
1533 //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str());
1534 ::strcpy((char*)&_buffer[offset], alias.c_str());
1535 offset += alias.size() + 1;
1541 // calculate start of text image array and trailing string pool
1542 dyld_cache_image_text_info
* textImages
= (dyld_cache_image_text_info
*)(_readExecuteRegion
.buffer
+ dyldCacheHeader
->imagesTextOffset
);
1543 uint32_t stringOffset
= (uint32_t)(dyldCacheHeader
->imagesTextOffset
+ sizeof(dyld_cache_image_text_info
) * _sortedDylibs
.size());
1545 // write text image array and image names pool at same time
1546 for (const DylibInfo
& dylib
: _sortedDylibs
) {
1547 dylib
.input
->mappedFile
.mh
->getUuid(textImages
->uuid
);
1548 textImages
->loadAddress
= dylib
.cacheLocation
[0].dstCacheUnslidAddress
;
1549 textImages
->textSegmentSize
= (uint32_t)dylib
.cacheLocation
[0].dstCacheSegmentSize
;
1550 textImages
->pathOffset
= stringOffset
;
1551 const char* installName
= dylib
.input
->mappedFile
.mh
->installName();
1552 ::strcpy((char*)_readExecuteRegion
.buffer
+ stringOffset
, installName
);
1553 stringOffset
+= (uint32_t)strlen(installName
)+1;
1557 // make sure header did not overflow into first mapped image
1558 const dyld_cache_image_info
* firstImage
= (dyld_cache_image_info
*)(_readExecuteRegion
.buffer
+ dyldCacheHeader
->imagesOffset
);
1559 assert(stringOffset
<= (firstImage
->address
- mappings
[0].address
));
1562 void SharedCacheBuilder::processSelectorStrings(const std::vector
<LoadedMachO
>& executables
, IMPCaches::HoleMap
& selectorsHoleMap
) {
1563 const bool log
= false;
1565 // We only do this optimisation to reduce the size of the shared cache executable closures
1566 // Skip this is those closures are not being built
1567 if ( !_options
.optimizeDyldDlopens
|| !_options
.optimizeDyldLaunches
)
1570 _selectorStringsFromExecutables
= 0;
1571 uint64_t totalBytesPulledIn
= 0;
1573 // Don't do this optimisation on watchOS where the shared cache is too small
1574 if (_options
.platform
== dyld3::Platform::watchOS
)
1577 // Get the method name coalesced section as that is where we need to put these strings
1578 CacheBuilder::CacheCoalescedText::StringSection
& cacheStringSection
= _coalescedText
.getSectionData("__objc_methname");
1579 for (const LoadedMachO
& executable
: executables
) {
1580 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)executable
.loadedFileInfo
.fileContent
;
1582 uint64_t sizeBeforeProcessing
= cacheStringSection
.bufferSize
;
1584 ma
->forEachObjCMethodName(^(const char* methodName
) {
1585 std::string_view str
= methodName
;
1586 if (cacheStringSection
.stringsToOffsets
.find(str
) == cacheStringSection
.stringsToOffsets
.end()) {
1587 int offset
= selectorsHoleMap
.addStringOfSize((unsigned)str
.size() + 1);
1588 cacheStringSection
.stringsToOffsets
[str
] = offset
;
1590 // If we inserted the string past the end then we need to include it in the total
1591 int possibleNewEnd
= offset
+ (int)str
.size() + 1;
1592 if (cacheStringSection
.bufferSize
< (uint32_t)possibleNewEnd
) {
1593 cacheStringSection
.bufferSize
= (uint32_t)possibleNewEnd
;
1595 // if (log) printf("Selector: %s -> %s\n", ma->installName(), methodName);
1596 ++_selectorStringsFromExecutables
;
1600 uint64_t sizeAfterProcessing
= cacheStringSection
.bufferSize
;
1601 totalBytesPulledIn
+= (sizeAfterProcessing
- sizeBeforeProcessing
);
1602 if ( log
&& (sizeBeforeProcessing
!= sizeAfterProcessing
) ) {
1603 printf("Pulled in % 6lld bytes of selectors from %s\n",
1604 sizeAfterProcessing
- sizeBeforeProcessing
, executable
.loadedFileInfo
.path
);
1608 _diagnostics
.verbose("Pulled in %lld selector strings (%lld bytes) from executables\n",
1609 _selectorStringsFromExecutables
, totalBytesPulledIn
);
1612 void SharedCacheBuilder::parseCoalescableSegments(IMPCaches::SelectorMap
& selectors
, IMPCaches::HoleMap
& selectorsHoleMap
) {
1613 const bool log
= false;
1615 for (DylibInfo
& dylib
: _sortedDylibs
)
1616 _coalescedText
.parseCoalescableText(dylib
.input
->mappedFile
.mh
, dylib
.textCoalescer
, selectors
, selectorsHoleMap
);
1619 for (const char* section
: CacheCoalescedText::SupportedSections
) {
1620 CacheCoalescedText::StringSection
& sectionData
= _coalescedText
.getSectionData(section
);
1621 printf("Coalesced %s from % 10lld -> % 10d, saving % 10lld bytes\n", section
,
1622 sectionData
.bufferSize
+ sectionData
.savedSpace
, sectionData
.bufferSize
, sectionData
.savedSpace
);
1626 // arm64e needs to convert CF constants to tagged pointers
1627 if ( !strcmp(_archLayout
->archName
, "arm64e") ) {
1628 // Find the dylib which exports the CFString ISA. It's likely CoreFoundation but it could move
1629 CacheCoalescedText::CFSection
& cfStrings
= _coalescedText
.cfStrings
;
1630 for (DylibInfo
& dylib
: _sortedDylibs
) {
1631 const dyld3::MachOAnalyzer
* ma
= dylib
.input
->mappedFile
.mh
;
1632 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
1633 bool foundISASymbol
= ma
->findExportedSymbol(_diagnostics
, cfStrings
.isaClassName
, false, foundInfo
, nullptr);
1634 if ( foundISASymbol
) {
1635 // This dylib exports the ISA, so everyone else should look here for the ISA too.
1636 if ( cfStrings
.isaInstallName
!= nullptr ) {
1637 // Found a duplicate. We can't do anything here
1638 _diagnostics
.verbose("Could not optimize CFString's due to duplicate ISA symbols");
1639 cfStrings
.isaInstallName
= nullptr;
1642 cfStrings
.isaInstallName
= ma
->installName();
1643 cfStrings
.isaVMOffset
= foundInfo
.value
;
1647 if ( cfStrings
.isaInstallName
!= nullptr ) {
1648 for (DylibInfo
& dylib
: _sortedDylibs
) {
1649 _coalescedText
.parseCFConstants(dylib
.input
->mappedFile
.mh
, dylib
.textCoalescer
);
1655 // This is the new method which will put all __DATA* mappings in to a their own mappings
1656 void SharedCacheBuilder::assignMultipleDataSegmentAddresses(uint64_t& addr
, uint32_t totalProtocolDefCount
) {
1657 uint64_t nextRegionFileOffset
= _readExecuteRegion
.sizeInUse
;
1659 const size_t dylibCount
= _sortedDylibs
.size();
1660 BLOCK_ACCCESSIBLE_ARRAY(uint32_t, dirtyDataSortIndexes
, dylibCount
);
1661 for (size_t i
=0; i
< dylibCount
; ++i
)
1662 dirtyDataSortIndexes
[i
] = (uint32_t)i
;
1663 std::sort(&dirtyDataSortIndexes
[0], &dirtyDataSortIndexes
[dylibCount
], [&](const uint32_t& a
, const uint32_t& b
) {
1664 const auto& orderA
= _options
.dirtyDataSegmentOrdering
.find(_sortedDylibs
[a
].input
->mappedFile
.runtimePath
);
1665 const auto& orderB
= _options
.dirtyDataSegmentOrdering
.find(_sortedDylibs
[b
].input
->mappedFile
.runtimePath
);
1666 bool foundA
= (orderA
!= _options
.dirtyDataSegmentOrdering
.end());
1667 bool foundB
= (orderB
!= _options
.dirtyDataSegmentOrdering
.end());
1669 // Order all __DATA_DIRTY segments specified in the order file first, in the order specified in the file,
1670 // followed by any other __DATA_DIRTY segments in lexicographic order.
1671 if ( foundA
&& foundB
)
1672 return orderA
->second
< orderB
->second
;
1678 return _sortedDylibs
[a
].input
->mappedFile
.runtimePath
< _sortedDylibs
[b
].input
->mappedFile
.runtimePath
;
1681 bool supportsAuthFixups
= false;
1683 // This tracks which segments contain authenticated data, even if their name isn't __AUTH*
1684 std::set
<uint32_t> authenticatedSegments
[dylibCount
];
1685 if ( strcmp(_archLayout
->archName
, "arm64e") == 0 ) {
1686 supportsAuthFixups
= true;
1688 for (DylibInfo
& dylib
: _sortedDylibs
) {
1689 uint64_t dylibIndex
= &dylib
- _sortedDylibs
.data();
1690 __block
std::set
<uint32_t>& authSegmentIndices
= authenticatedSegments
[dylibIndex
];
1692 // Put all __DATA_DIRTY segments in the __AUTH region first, then we don't need to walk their chains
1693 dylib
.input
->mappedFile
.mh
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& segInfo
, bool& stop
) {
1694 if ( strcmp(segInfo
.segName
, "__DATA_DIRTY") == 0 ) {
1695 authSegmentIndices
.insert(segInfo
.segIndex
);
1699 dylib
.input
->mappedFile
.mh
->withChainStarts(_diagnostics
, 0,
1700 ^(const dyld_chained_starts_in_image
*starts
) {
1701 dylib
.input
->mappedFile
.mh
->forEachFixupChainSegment(_diagnostics
, starts
,
1702 ^(const dyld_chained_starts_in_segment
* segmentInfo
, uint32_t segIndex
, bool& stopSegment
) {
1703 // Skip walking segments we already know are __AUTH, ie, __DATA_DIRTY
1704 if ( authSegmentIndices
.count(segIndex
) )
1707 dylib
.input
->mappedFile
.mh
->forEachFixupInSegmentChains(_diagnostics
, segmentInfo
, false,
1708 ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* fixupLoc
, const dyld_chained_starts_in_segment
* segInfo
, bool& stopChain
) {
1709 uint16_t chainedFixupsFormat
= segInfo
->pointer_format
;
1710 assert( (chainedFixupsFormat
== DYLD_CHAINED_PTR_ARM64E
) || (chainedFixupsFormat
== DYLD_CHAINED_PTR_ARM64E_USERLAND
) || (chainedFixupsFormat
== DYLD_CHAINED_PTR_ARM64E_USERLAND24
) );
1712 if ( fixupLoc
->arm64e
.authRebase
.auth
) {
1713 authSegmentIndices
.insert(segIndex
);
1723 // Categorize each segment in each binary
1724 enum class SegmentType
: uint8_t {
1725 skip
, // used for non-data segments we should ignore here
1734 BLOCK_ACCCESSIBLE_ARRAY(uint64_t, textSegVmAddrs
, dylibCount
);
1735 BLOCK_ACCCESSIBLE_ARRAY(std::vector
<SegmentType
>, segmentTypes
, dylibCount
);
1737 // Just in case __AUTH is used in a non-arm64e binary, we can force it to use data enums
1738 SegmentType authSegment
= supportsAuthFixups
? SegmentType::auth
: SegmentType::data
;
1739 SegmentType authConstSegment
= supportsAuthFixups
? SegmentType::authConst
: SegmentType::dataConst
;
1741 for (const DylibInfo
& dylib
: _sortedDylibs
) {
1742 uint64_t dylibIndex
= &dylib
- _sortedDylibs
.data();
1743 __block
std::set
<uint32_t>& authSegmentIndices
= authenticatedSegments
[dylibIndex
];
1744 __block
std::vector
<SegmentType
>& dylibSegmentTypes
= segmentTypes
[dylibIndex
];
1745 uint64_t &textSegVmAddr
= textSegVmAddrs
[dylibIndex
];
1746 dylib
.input
->mappedFile
.mh
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& segInfo
, bool& stop
) {
1747 if ( strcmp(segInfo
.segName
, "__TEXT") == 0 ) {
1748 textSegVmAddr
= segInfo
.vmAddr
;
1751 // Skip non-DATA segments
1752 if ( segInfo
.protections
!= (VM_PROT_READ
| VM_PROT_WRITE
) ) {
1753 dylibSegmentTypes
.push_back(SegmentType::skip
);
1757 // If we don't have split seg v2, then all remaining segments must look like __DATA so that they
1759 if (!dylib
.input
->mappedFile
.mh
->isSplitSegV2()) {
1760 dylibSegmentTypes
.push_back(SegmentType::data
);
1764 __block
bool supportsDataConst
= true;
1765 if ( dylib
.input
->mappedFile
.mh
->isSwiftLibrary() ) {
1766 uint64_t objcConstSize
= 0;
1767 bool containsObjCSection
= dylib
.input
->mappedFile
.mh
->findSectionContent(segInfo
.segName
, "__objc_const", objcConstSize
);
1769 // <rdar://problem/66284631> Don't put __objc_const read-only memory as Swift has method lists we can't see
1770 if ( containsObjCSection
)
1771 supportsDataConst
= false;
1772 } else if ( !strcmp(dylib
.input
->mappedFile
.mh
->installName(), "/System/Library/Frameworks/Foundation.framework/Foundation") ||
1773 !strcmp(dylib
.input
->mappedFile
.mh
->installName(), "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") ) {
1774 // <rdar://problem/69813664> _NSTheOneTruePredicate is incompatible with __DATA_CONST
1775 supportsDataConst
= false;
1776 } else if ( !strcmp(dylib
.input
->mappedFile
.mh
->installName(), "/usr/lib/system/libdispatch.dylib") ) {
1777 // rdar://72361509 (Speechrecognitiond crashing on AzulE18E123)
1778 supportsDataConst
= false;
1779 } else if ( !strcmp(dylib
.input
->mappedFile
.mh
->installName(), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation") ||
1780 !strcmp(dylib
.input
->mappedFile
.mh
->installName(), "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation") ) {
1781 // rdar://74112547 CF writes to kCFNull constant object
1782 supportsDataConst
= false;
1785 // Don't use data const for dylibs containing resolver functions. This will be fixed in ld64 by moving their pointer atoms to __DATA
1786 if ( supportsDataConst
&& endsWith(segInfo
.segName
, "_CONST") ) {
1787 dylib
.input
->mappedFile
.mh
->forEachExportedSymbol(_diagnostics
,
1788 ^(const char *symbolName
, uint64_t imageOffset
, uint64_t flags
, uint64_t other
, const char *importName
, bool &stop
) {
1789 if ( (flags
& EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
) != 0 ) {
1790 _diagnostics
.verbose("%s: preventing use of __DATA_CONST due to resolvers\n", dylib
.dylibID
.c_str());
1791 supportsDataConst
= false;
1797 // If we are still allowed to use __DATA_CONST, then make sure that we are not using pointer based method lists. These may not be written in libobjc due
1798 // to uniquing or sorting (as those are done in the builder), but clients can still call setIMP to mutate them.
1799 if ( supportsDataConst
&& endsWith(segInfo
.segName
, "_CONST") ) {
1800 uint64_t segStartVMAddr
= segInfo
.vmAddr
;
1801 uint64_t segEndVMAddr
= segInfo
.vmAddr
+ segInfo
.vmSize
;
1803 auto vmAddrConverter
= dylib
.input
->mappedFile
.mh
->makeVMAddrConverter(false);
1804 const uint32_t pointerSize
= dylib
.input
->mappedFile
.mh
->pointerSize();
1806 __block
bool foundPointerBasedMethodList
= false;
1807 auto visitMethodList
= ^(uint64_t methodListVMAddr
) {
1808 if ( foundPointerBasedMethodList
)
1810 if ( methodListVMAddr
== 0 )
1812 // Ignore method lists in other segments
1813 if ( (methodListVMAddr
< segStartVMAddr
) || (methodListVMAddr
>= segEndVMAddr
) )
1815 auto visitMethod
= ^(uint64_t methodVMAddr
, const dyld3::MachOAnalyzer::ObjCMethod
& method
) { };
1816 bool isRelativeMethodList
= false;
1817 dylib
.input
->mappedFile
.mh
->forEachObjCMethod(methodListVMAddr
, vmAddrConverter
, visitMethod
, &isRelativeMethodList
);
1818 if ( !isRelativeMethodList
)
1819 foundPointerBasedMethodList
= true;
1822 auto visitClass
= ^(Diagnostics
& diag
, uint64_t classVMAddr
,
1823 uint64_t classSuperclassVMAddr
, uint64_t classDataVMAddr
,
1824 const dyld3::MachOAnalyzer::ObjCClassInfo
& objcClass
, bool isMetaClass
) {
1825 visitMethodList(objcClass
.baseMethodsVMAddr(pointerSize
));
1828 auto visitCategory
= ^(Diagnostics
& diag
, uint64_t categoryVMAddr
,
1829 const dyld3::MachOAnalyzer::ObjCCategory
& objcCategory
) {
1830 visitMethodList(objcCategory
.instanceMethodsVMAddr
);
1831 visitMethodList(objcCategory
.classMethodsVMAddr
);
1834 // Walk the class list
1835 Diagnostics classDiag
;
1836 dylib
.input
->mappedFile
.mh
->forEachObjCClass(classDiag
, vmAddrConverter
, visitClass
);
1838 // Walk the category list
1839 Diagnostics categoryDiag
;
1840 dylib
.input
->mappedFile
.mh
->forEachObjCCategory(categoryDiag
, vmAddrConverter
, visitCategory
);
1842 // Note we don't walk protocols as they don't have an IMP to set
1844 if ( foundPointerBasedMethodList
) {
1845 _diagnostics
.verbose("%s: preventing use of read-only %s due to pointer based method list\n", dylib
.dylibID
.c_str(), segInfo
.segName
);
1846 supportsDataConst
= false;
1851 if ( strcmp(segInfo
.segName
, "__AUTH_CONST") == 0 ) {
1852 dylibSegmentTypes
.push_back(supportsDataConst
? authConstSegment
: authSegment
);
1857 if ( (strcmp(segInfo
.segName
, "__DATA_CONST") == 0) || (strcmp(segInfo
.segName
, "__OBJC_CONST") == 0) ) {
1858 if ( authSegmentIndices
.count(segInfo
.segIndex
) ) {
1859 // _diagnostics.verbose("%s: treating authenticated %s as __AUTH_CONST\n", dylib.dylibID.c_str(), segInfo.segName);
1860 dylibSegmentTypes
.push_back(supportsDataConst
? SegmentType::authConst
: SegmentType::auth
);
1862 dylibSegmentTypes
.push_back(supportsDataConst
? SegmentType::dataConst
: SegmentType::data
);
1868 if ( strcmp(segInfo
.segName
, "__DATA_DIRTY") == 0 ) {
1869 if ( authSegmentIndices
.count(segInfo
.segIndex
) ) {
1870 dylibSegmentTypes
.push_back(SegmentType::authDirty
);
1872 dylibSegmentTypes
.push_back(SegmentType::dataDirty
);
1878 if ( strcmp(segInfo
.segName
, "__AUTH") == 0 ) {
1879 dylibSegmentTypes
.push_back(authSegment
);
1884 if ( authSegmentIndices
.count(segInfo
.segIndex
) ) {
1885 // _diagnostics.verbose("%s: treating authenticated %s as __AUTH\n", dylib.dylibID.c_str(), segInfo.segName);
1886 dylibSegmentTypes
.push_back(SegmentType::auth
);
1888 dylibSegmentTypes
.push_back(SegmentType::data
);
1893 auto processDylibSegments
= ^(SegmentType onlyType
, Region
& region
) {
1894 for (size_t unsortedDylibIndex
= 0; unsortedDylibIndex
!= dylibCount
; ++unsortedDylibIndex
) {
1895 size_t dylibIndex
= unsortedDylibIndex
;
1896 if ( (onlyType
== SegmentType::dataDirty
) || (onlyType
== SegmentType::authDirty
) )
1897 dylibIndex
= dirtyDataSortIndexes
[dylibIndex
];
1899 DylibInfo
& dylib
= _sortedDylibs
[dylibIndex
];
1900 const std::vector
<SegmentType
>& dylibSegmentTypes
= segmentTypes
[dylibIndex
];
1901 const uint64_t textSegVmAddr
= textSegVmAddrs
[dylibIndex
];
1903 bool forcePageAlignedData
= false;
1904 if ( (_options
.platform
== dyld3::Platform::macOS
) && (onlyType
== SegmentType::data
) ) {
1905 forcePageAlignedData
= dylib
.input
->mappedFile
.mh
->hasUnalignedPointerFixups();
1906 //if ( forcePageAlignedData )
1907 // warning("unaligned pointer in %s\n", dylib.input->mappedFile.runtimePath.c_str());
1910 dylib
.input
->mappedFile
.mh
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& segInfo
, bool& stop
) {
1911 if ( dylibSegmentTypes
[segInfo
.segIndex
] != onlyType
)
1914 // We may have coalesced the sections at the end of this segment. In that case, shrink the segment to remove them.
1915 __block
size_t sizeOfSections
= 0;
1916 __block
bool foundCoalescedSection
= false;
1917 dylib
.input
->mappedFile
.mh
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stopSection
) {
1918 if (strcmp(sectInfo
.segInfo
.segName
, segInfo
.segName
) != 0)
1920 if ( dylib
.textCoalescer
.sectionWasCoalesced(segInfo
.segName
, sectInfo
.sectName
)) {
1921 foundCoalescedSection
= true;
1923 sizeOfSections
= sectInfo
.sectAddr
+ sectInfo
.sectSize
- segInfo
.vmAddr
;
1926 if (!foundCoalescedSection
)
1927 sizeOfSections
= segInfo
.sizeOfSections
;
1929 if ( !forcePageAlignedData
) {
1930 // Pack __DATA segments
1931 addr
= align(addr
, segInfo
.p2align
);
1934 // Keep __DATA segments 4K or more aligned
1935 addr
= align(addr
, std::max((int)segInfo
.p2align
, (int)12));
1938 size_t copySize
= std::min((size_t)segInfo
.fileSize
, (size_t)sizeOfSections
);
1939 uint64_t offsetInRegion
= addr
- region
.unslidLoadAddress
;
1940 SegmentMappingInfo loc
;
1941 loc
.srcSegment
= (uint8_t*)dylib
.input
->mappedFile
.mh
+ segInfo
.vmAddr
- textSegVmAddr
;
1942 loc
.segName
= segInfo
.segName
;
1943 loc
.dstSegment
= region
.buffer
+ offsetInRegion
;
1944 loc
.dstCacheUnslidAddress
= addr
;
1945 loc
.dstCacheFileOffset
= (uint32_t)(region
.cacheFileOffset
+ offsetInRegion
);
1946 loc
.dstCacheSegmentSize
= (uint32_t)sizeOfSections
;
1947 loc
.dstCacheFileSize
= (uint32_t)copySize
;
1948 loc
.copySegmentSize
= (uint32_t)copySize
;
1949 loc
.srcSegmentIndex
= segInfo
.segIndex
;
1950 dylib
.cacheLocation
.push_back(loc
);
1951 addr
+= loc
.dstCacheSegmentSize
;
1956 addr
= align(addr
, _archLayout
->sharedRegionAlignP2
);
1960 const char* regionName
;
1961 SegmentType dataSegment
;
1962 std::optional
<SegmentType
> dirtySegment
;
1963 // Note this is temporary as once all platforms/archs support __DATA_CONST, we can move to a DataRegion just for CONST
1964 std::optional
<SegmentType
> dataConstSegment
;
1968 std::vector
<DataRegion
> dataRegions
;
1970 // We only support __DATA_CONST on arm64(e) for now.
1971 bool supportDataConst
= false;
1972 //supportDataConst |= strcmp(_archLayout->archName, "arm64") == 0;
1973 supportDataConst
|= strcmp(_archLayout
->archName
, "arm64e") == 0;
1974 if ( supportDataConst
) {
1975 bool addObjCRWToData
= !supportsAuthFixups
;
1976 DataRegion dataWriteRegion
= { "__DATA", SegmentType::data
, SegmentType::dataDirty
, {}, false, addObjCRWToData
};
1977 DataRegion dataConstRegion
= { "__DATA_CONST", SegmentType::dataConst
, {}, {}, true, false };
1978 DataRegion authWriteRegion
= { "__AUTH", SegmentType::auth
, SegmentType::authDirty
, {}, false, !addObjCRWToData
};
1979 DataRegion authConstRegion
= { "__AUTH_CONST", SegmentType::authConst
, {}, {}, false, false };
1980 dataRegions
.push_back(dataWriteRegion
);
1981 dataRegions
.push_back(dataConstRegion
);
1982 if ( supportsAuthFixups
) {
1983 dataRegions
.push_back(authWriteRegion
);
1984 dataRegions
.push_back(authConstRegion
);
1987 DataRegion dataWriteRegion
= { "__DATA", SegmentType::data
, SegmentType::dataDirty
, SegmentType::dataConst
, false, true };
1988 dataRegions
.push_back(dataWriteRegion
);
1991 for (DataRegion
& dataRegion
: dataRegions
)
1994 region
.buffer
= (uint8_t*)_fullAllocatedBuffer
+ addr
- _archLayout
->sharedMemoryStart
;
1995 region
.bufferSize
= 0;
1996 region
.sizeInUse
= 0;
1997 region
.unslidLoadAddress
= addr
;
1998 region
.cacheFileOffset
= nextRegionFileOffset
;
1999 region
.name
= dataRegion
.regionName
;
2000 region
.initProt
= endsWith(dataRegion
.regionName
, "_CONST") ? VM_PROT_READ
: (VM_PROT_READ
| VM_PROT_WRITE
);
2001 region
.maxProt
= VM_PROT_READ
| VM_PROT_WRITE
;
2003 // layout all __DATA_DIRTY segments, sorted (FIXME)
2004 if (dataRegion
.dirtySegment
.has_value())
2005 processDylibSegments(*dataRegion
.dirtySegment
, region
);
2007 // layout all __DATA segments (and other r/w non-dirty, non-const, non-auth) segments
2008 processDylibSegments(dataRegion
.dataSegment
, region
);
2010 // When __DATA_CONST is not its own DataRegion, we fold it in to the __DATA DataRegion
2011 if (dataRegion
.dataConstSegment
.has_value())
2012 processDylibSegments(*dataRegion
.dataConstSegment
, region
);
2014 // Make space for the cfstrings
2015 if ( (dataRegion
.addCFStrings
) && (_coalescedText
.cfStrings
.bufferSize
!= 0) ) {
2016 // Keep __DATA segments 4K or more aligned
2017 addr
= align(addr
, 12);
2018 uint64_t offsetInRegion
= addr
- region
.unslidLoadAddress
;
2020 CacheCoalescedText::CFSection
& cacheSection
= _coalescedText
.cfStrings
;
2021 cacheSection
.bufferAddr
= region
.buffer
+ offsetInRegion
;
2022 cacheSection
.bufferVMAddr
= addr
;
2023 cacheSection
.cacheFileOffset
= region
.cacheFileOffset
+ offsetInRegion
;
2024 addr
+= cacheSection
.bufferSize
;
2027 if ( dataRegion
.addObjCRW
) {
2028 // reserve space for objc r/w optimization tables
2029 _objcReadWriteBufferSizeAllocated
= align(computeReadWriteObjC((uint32_t)_sortedDylibs
.size(), totalProtocolDefCount
), 14);
2030 addr
= align(addr
, 4); // objc r/w section contains pointer and must be at least pointer align
2031 _objcReadWriteBuffer
= region
.buffer
+ (addr
- region
.unslidLoadAddress
);
2032 _objcReadWriteFileOffset
= (uint32_t)((_objcReadWriteBuffer
- region
.buffer
) + region
.cacheFileOffset
);
2033 addr
+= _objcReadWriteBufferSizeAllocated
;
2037 addr
= align(addr
, _archLayout
->sharedRegionAlignP2
);
2040 // align DATA region end
2041 uint64_t endDataAddress
= addr
;
2042 region
.bufferSize
= endDataAddress
- region
.unslidLoadAddress
;
2043 region
.sizeInUse
= region
.bufferSize
;
2045 _dataRegions
.push_back(region
);
2046 nextRegionFileOffset
= region
.cacheFileOffset
+ region
.sizeInUse
;
2048 // Only arm64 and arm64e shared caches have enough space to pad between __DATA and __DATA_CONST
2049 // All other caches are overflowing.
2050 if ( !strcmp(_archLayout
->archName
, "arm64") || !strcmp(_archLayout
->archName
, "arm64e") )
2051 addr
= align((addr
+ _archLayout
->sharedRegionPadding
), _archLayout
->sharedRegionAlignP2
);
2054 // Sanity check that we didn't put the same segment in 2 different ranges
2055 for (DylibInfo
& dylib
: _sortedDylibs
) {
2056 std::unordered_set
<uint64_t> seenSegmentIndices
;
2057 for (SegmentMappingInfo
& segmentInfo
: dylib
.cacheLocation
) {
2058 if ( seenSegmentIndices
.count(segmentInfo
.srcSegmentIndex
) != 0 ) {
2059 _diagnostics
.error("%s segment %s was duplicated in layout",
2060 dylib
.input
->mappedFile
.mh
->installName(), segmentInfo
.segName
);
2063 seenSegmentIndices
.insert(segmentInfo
.srcSegmentIndex
);
2068 void SharedCacheBuilder::assignSegmentAddresses()
2070 // calculate size of header info and where first dylib's mach_header should start
2071 size_t startOffset
= sizeof(dyld_cache_header
) + DyldSharedCache::MaxMappings
* sizeof(dyld_cache_mapping_info
);
2072 startOffset
+= DyldSharedCache::MaxMappings
* sizeof(dyld_cache_mapping_and_slide_info
);
2073 startOffset
+= sizeof(dyld_cache_image_info
) * _sortedDylibs
.size();
2074 startOffset
+= sizeof(dyld_cache_image_text_info
) * _sortedDylibs
.size();
2075 for (const DylibInfo
& dylib
: _sortedDylibs
) {
2076 startOffset
+= (strlen(dylib
.input
->mappedFile
.mh
->installName()) + 1);
2078 //fprintf(stderr, "%s total header size = 0x%08lX\n", _options.archName.c_str(), startOffset);
2079 startOffset
= align(startOffset
, 12);
2081 // HACK!: Rebase v4 assumes that values below 0x8000 are not pointers (encoding as offsets from the cache header).
2082 // If using a minimal cache, we need to pad out the cache header to make sure a pointer doesn't fall within that range
2083 #if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
2084 if ( _options
.cacheSupportsASLR
&& !_archLayout
->is64
) {
2085 if ( _archLayout
->pointerDeltaMask
== 0xC0000000 )
2086 startOffset
= std::max(startOffset
, (size_t)0x8000);
2090 // assign TEXT segment addresses
2091 _readExecuteRegion
.buffer
= (uint8_t*)_fullAllocatedBuffer
;
2092 _readExecuteRegion
.bufferSize
= 0;
2093 _readExecuteRegion
.sizeInUse
= 0;
2094 _readExecuteRegion
.unslidLoadAddress
= _archLayout
->sharedMemoryStart
;
2095 _readExecuteRegion
.cacheFileOffset
= 0;
2096 __block
uint64_t addr
= _readExecuteRegion
.unslidLoadAddress
+ startOffset
; // header
2097 for (DylibInfo
& dylib
: _sortedDylibs
) {
2098 __block
uint64_t textSegVmAddr
= 0;
2099 dylib
.input
->mappedFile
.mh
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& segInfo
, bool& stop
) {
2100 if ( strcmp(segInfo
.segName
, "__TEXT") == 0 )
2101 textSegVmAddr
= segInfo
.vmAddr
;
2102 if ( segInfo
.protections
!= (VM_PROT_READ
| VM_PROT_EXECUTE
) )
2104 // We may have coalesced the sections at the end of this segment. In that case, shrink the segment to remove them.
2105 __block
size_t sizeOfSections
= 0;
2106 __block
bool foundCoalescedSection
= false;
2107 dylib
.input
->mappedFile
.mh
->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo
§Info
, bool malformedSectionRange
, bool &stopSection
) {
2108 if (strcmp(sectInfo
.segInfo
.segName
, segInfo
.segName
) != 0)
2110 if ( dylib
.textCoalescer
.sectionWasCoalesced(segInfo
.segName
, sectInfo
.sectName
)) {
2111 foundCoalescedSection
= true;
2113 sizeOfSections
= sectInfo
.sectAddr
+ sectInfo
.sectSize
- segInfo
.vmAddr
;
2116 if (!foundCoalescedSection
)
2117 sizeOfSections
= segInfo
.sizeOfSections
;
2119 // Keep __TEXT segments 4K or more aligned
2120 addr
= align(addr
, std::max((int)segInfo
.p2align
, (int)12));
2121 uint64_t offsetInRegion
= addr
- _readExecuteRegion
.unslidLoadAddress
;
2122 SegmentMappingInfo loc
;
2123 loc
.srcSegment
= (uint8_t*)dylib
.input
->mappedFile
.mh
+ segInfo
.vmAddr
- textSegVmAddr
;
2124 loc
.segName
= segInfo
.segName
;
2125 loc
.dstSegment
= _readExecuteRegion
.buffer
+ offsetInRegion
;
2126 loc
.dstCacheUnslidAddress
= addr
;
2127 loc
.dstCacheFileOffset
= (uint32_t)offsetInRegion
;
2128 loc
.dstCacheSegmentSize
= (uint32_t)align(sizeOfSections
, 12);
2129 loc
.dstCacheFileSize
= (uint32_t)align(sizeOfSections
, 12);
2130 loc
.copySegmentSize
= (uint32_t)sizeOfSections
;
2131 loc
.srcSegmentIndex
= segInfo
.segIndex
;
2132 dylib
.cacheLocation
.push_back(loc
);
2133 addr
+= loc
.dstCacheSegmentSize
;
2137 // reserve space for objc optimization tables and deduped strings
2138 uint64_t objcReadOnlyBufferVMAddr
= addr
;
2139 _objcReadOnlyBuffer
= _readExecuteRegion
.buffer
+ (addr
- _readExecuteRegion
.unslidLoadAddress
);
2141 // First the strings as we'll fill in the objc tables later in the optimizer
2142 for (const char* section
: CacheCoalescedText::SupportedSections
) {
2143 CacheCoalescedText::StringSection
& cacheStringSection
= _coalescedText
.getSectionData(section
);
2144 cacheStringSection
.bufferAddr
= _readExecuteRegion
.buffer
+ (addr
- _readExecuteRegion
.unslidLoadAddress
);
2145 cacheStringSection
.bufferVMAddr
= addr
;
2146 addr
+= cacheStringSection
.bufferSize
;
2149 addr
= align(addr
, 14);
2150 _objcReadOnlyBufferSizeUsed
= addr
- objcReadOnlyBufferVMAddr
;
2152 uint32_t totalSelectorRefCount
= (uint32_t)_selectorStringsFromExecutables
;
2153 uint32_t totalClassDefCount
= 0;
2154 uint32_t totalProtocolDefCount
= 0;
2155 for (DylibInfo
& dylib
: _sortedDylibs
) {
2156 dyld3::MachOAnalyzer::ObjCInfo info
= dylib
.input
->mappedFile
.mh
->getObjCInfo();
2157 totalSelectorRefCount
+= info
.selRefCount
;
2158 totalClassDefCount
+= info
.classDefCount
;
2159 totalProtocolDefCount
+= info
.protocolDefCount
;
2162 // now that shared cache coalesces all selector strings, use that better count
2163 uint32_t coalescedSelectorCount
= (uint32_t)_coalescedText
.objcMethNames
.stringsToOffsets
.size();
2164 if ( coalescedSelectorCount
> totalSelectorRefCount
)
2165 totalSelectorRefCount
= coalescedSelectorCount
;
2166 addr
+= align(computeReadOnlyObjC(totalSelectorRefCount
, totalClassDefCount
, totalProtocolDefCount
), 14);
2168 size_t impCachesSize
= _impCachesBuilder
->totalIMPCachesSize();
2169 size_t alignedImpCachesSize
= align(impCachesSize
, 14);
2170 _diagnostics
.verbose("Reserving %zd bytes for IMP caches (aligned to %zd)\n", impCachesSize
, alignedImpCachesSize
);
2171 addr
+= alignedImpCachesSize
;
2173 _objcReadOnlyBufferSizeAllocated
= addr
- objcReadOnlyBufferVMAddr
;
2175 // align TEXT region end
2176 uint64_t endTextAddress
= align(addr
, _archLayout
->sharedRegionAlignP2
);
2177 _readExecuteRegion
.bufferSize
= endTextAddress
- _readExecuteRegion
.unslidLoadAddress
;
2178 _readExecuteRegion
.sizeInUse
= _readExecuteRegion
.bufferSize
;
2181 // assign __DATA* addresses
2182 if ( _archLayout
->sharedRegionsAreDiscontiguous
)
2183 addr
= DISCONTIGUOUS_RW
;
2185 addr
= align((addr
+ _archLayout
->sharedRegionPadding
), _archLayout
->sharedRegionAlignP2
);
2188 assignMultipleDataSegmentAddresses(addr
, totalProtocolDefCount
);
2190 // start read-only region
2191 if ( _archLayout
->sharedRegionsAreDiscontiguous
)
2192 addr
= DISCONTIGUOUS_RO
;
2194 addr
= align((addr
+ _archLayout
->sharedRegionPadding
), _archLayout
->sharedRegionAlignP2
);
2195 _readOnlyRegion
.buffer
= (uint8_t*)_fullAllocatedBuffer
+ addr
- _archLayout
->sharedMemoryStart
;
2196 _readOnlyRegion
.bufferSize
= 0;
2197 _readOnlyRegion
.sizeInUse
= 0;
2198 _readOnlyRegion
.unslidLoadAddress
= addr
;
2199 _readOnlyRegion
.cacheFileOffset
= lastDataRegion()->cacheFileOffset
+ lastDataRegion()->sizeInUse
;
2202 // reserve space for kernel ASLR slide info at start of r/o region
2203 if ( _options
.cacheSupportsASLR
) {
2204 size_t slideInfoSize
= sizeof(dyld_cache_slide_info
);
2205 slideInfoSize
= std::max(slideInfoSize
, sizeof(dyld_cache_slide_info2
));
2206 slideInfoSize
= std::max(slideInfoSize
, sizeof(dyld_cache_slide_info3
));
2207 slideInfoSize
= std::max(slideInfoSize
, sizeof(dyld_cache_slide_info4
));
2208 // We need one slide info header per data region, plus enough space for that regions pages
2209 // Each region will also be padded to a page-size so that the kernel can wire it.
2210 for (Region
& region
: _dataRegions
) {
2211 uint64_t offsetInRegion
= addr
- _readOnlyRegion
.unslidLoadAddress
;
2212 region
.slideInfoBuffer
= _readOnlyRegion
.buffer
+ offsetInRegion
;
2213 region
.slideInfoBufferSizeAllocated
= align(slideInfoSize
+ (region
.sizeInUse
/4096) * _archLayout
->slideInfoBytesPerPage
+ 0x4000, _archLayout
->sharedRegionAlignP2
);
2214 region
.slideInfoFileOffset
= _readOnlyRegion
.cacheFileOffset
+ offsetInRegion
;
2215 addr
+= region
.slideInfoBufferSizeAllocated
;
2219 // layout all read-only (but not LINKEDIT) segments
2220 for (DylibInfo
& dylib
: _sortedDylibs
) {
2221 __block
uint64_t textSegVmAddr
= 0;
2222 dylib
.input
->mappedFile
.mh
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& segInfo
, bool& stop
) {
2223 if ( strcmp(segInfo
.segName
, "__TEXT") == 0 )
2224 textSegVmAddr
= segInfo
.vmAddr
;
2225 if ( segInfo
.protections
!= VM_PROT_READ
)
2227 if ( strcmp(segInfo
.segName
, "__LINKEDIT") == 0 )
2230 // Keep segments segments 4K or more aligned
2231 addr
= align(addr
, std::max((int)segInfo
.p2align
, (int)12));
2232 uint64_t offsetInRegion
= addr
- _readOnlyRegion
.unslidLoadAddress
;
2233 SegmentMappingInfo loc
;
2234 loc
.srcSegment
= (uint8_t*)dylib
.input
->mappedFile
.mh
+ segInfo
.vmAddr
- textSegVmAddr
;
2235 loc
.segName
= segInfo
.segName
;
2236 loc
.dstSegment
= _readOnlyRegion
.buffer
+ offsetInRegion
;
2237 loc
.dstCacheUnslidAddress
= addr
;
2238 loc
.dstCacheFileOffset
= (uint32_t)(_readOnlyRegion
.cacheFileOffset
+ offsetInRegion
);
2239 loc
.dstCacheSegmentSize
= (uint32_t)align(segInfo
.sizeOfSections
, 12);
2240 loc
.dstCacheFileSize
= (uint32_t)segInfo
.sizeOfSections
;
2241 loc
.copySegmentSize
= (uint32_t)segInfo
.sizeOfSections
;
2242 loc
.srcSegmentIndex
= segInfo
.segIndex
;
2243 dylib
.cacheLocation
.push_back(loc
);
2244 addr
+= loc
.dstCacheSegmentSize
;
2248 // layout all LINKEDIT segments (after other read-only segments), aligned to 16KB
2249 addr
= align(addr
, 14);
2250 _nonLinkEditReadOnlySize
= addr
- _readOnlyRegion
.unslidLoadAddress
;
2251 for (DylibInfo
& dylib
: _sortedDylibs
) {
2252 __block
uint64_t textSegVmAddr
= 0;
2253 dylib
.input
->mappedFile
.mh
->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& segInfo
, bool& stop
) {
2254 if ( strcmp(segInfo
.segName
, "__TEXT") == 0 )
2255 textSegVmAddr
= segInfo
.vmAddr
;
2256 if ( segInfo
.protections
!= VM_PROT_READ
)
2258 if ( strcmp(segInfo
.segName
, "__LINKEDIT") != 0 )
2260 // Keep segments segments 4K or more aligned
2261 addr
= align(addr
, std::max((int)segInfo
.p2align
, (int)12));
2262 size_t copySize
= std::min((size_t)segInfo
.fileSize
, (size_t)segInfo
.sizeOfSections
);
2263 uint64_t offsetInRegion
= addr
- _readOnlyRegion
.unslidLoadAddress
;
2264 SegmentMappingInfo loc
;
2265 loc
.srcSegment
= (uint8_t*)dylib
.input
->mappedFile
.mh
+ segInfo
.vmAddr
- textSegVmAddr
;
2266 loc
.segName
= segInfo
.segName
;
2267 loc
.dstSegment
= _readOnlyRegion
.buffer
+ offsetInRegion
;
2268 loc
.dstCacheUnslidAddress
= addr
;
2269 loc
.dstCacheFileOffset
= (uint32_t)(_readOnlyRegion
.cacheFileOffset
+ offsetInRegion
);
2270 loc
.dstCacheSegmentSize
= (uint32_t)align(segInfo
.sizeOfSections
, 12);
2271 loc
.dstCacheFileSize
= (uint32_t)copySize
;
2272 loc
.copySegmentSize
= (uint32_t)copySize
;
2273 loc
.srcSegmentIndex
= segInfo
.segIndex
;
2274 dylib
.cacheLocation
.push_back(loc
);
2275 addr
+= loc
.dstCacheSegmentSize
;
2279 // align r/o region end
2280 addr
= align(addr
, _archLayout
->sharedRegionAlignP2
);
2281 uint64_t endReadOnlyAddress
= addr
;
2282 _readOnlyRegion
.bufferSize
= endReadOnlyAddress
- _readOnlyRegion
.unslidLoadAddress
+ 0x100000;
2283 _readOnlyRegion
.sizeInUse
= _readOnlyRegion
.bufferSize
;
2285 //fprintf(stderr, "RX region=%p -> %p, logical addr=0x%llX\n", _readExecuteRegion.buffer, _readExecuteRegion.buffer+_readExecuteRegion.bufferSize, _readExecuteRegion.unslidLoadAddress);
2286 //fprintf(stderr, "RW region=%p -> %p, logical addr=0x%llX\n", readWriteRegion.buffer, readWriteRegion.buffer+readWriteRegion.bufferSize, readWriteRegion.unslidLoadAddress);
2287 //fprintf(stderr, "RO region=%p -> %p, logical addr=0x%llX\n", _readOnlyRegion.buffer, _readOnlyRegion.buffer+_readOnlyRegion.bufferSize, _readOnlyRegion.unslidLoadAddress);
2289 // sort SegmentMappingInfo for each image to be in the same order as original segments
2290 for (DylibInfo
& dylib
: _sortedDylibs
) {
2291 std::sort(dylib
.cacheLocation
.begin(), dylib
.cacheLocation
.end(), [&](const SegmentMappingInfo
& a
, const SegmentMappingInfo
& b
) {
2292 return a
.srcSegmentIndex
< b
.srcSegmentIndex
;
2297 // Return the total size of the data regions, including padding between them.
2298 // Note this assumes they are contiguous, or that we don't care about including
2299 // additional space between them.
2300 uint64_t SharedCacheBuilder::dataRegionsTotalSize() const {
2301 const Region
* firstRegion
= nullptr;
2302 const Region
* lastRegion
= nullptr;
2303 for (const Region
& region
: _dataRegions
) {
2304 if ( (firstRegion
== nullptr) || (region
.buffer
< firstRegion
->buffer
) )
2305 firstRegion
= ®ion
;
2306 if ( (lastRegion
== nullptr) || (region
.buffer
> lastRegion
->buffer
) )
2307 lastRegion
= ®ion
;
2309 return (lastRegion
->buffer
- firstRegion
->buffer
) + lastRegion
->sizeInUse
;
2313 // Return the total size of the data regions, excluding padding between them
2314 uint64_t SharedCacheBuilder::dataRegionsSizeInUse() const {
2316 for (const Region
& dataRegion
: _dataRegions
)
2317 size
+= dataRegion
.sizeInUse
;
2321 // Return the earliest data region by address
2322 const CacheBuilder::Region
* SharedCacheBuilder::firstDataRegion() const {
2323 const Region
* firstRegion
= nullptr;
2324 for (const Region
& region
: _dataRegions
) {
2325 if ( (firstRegion
== nullptr) || (region
.buffer
< firstRegion
->buffer
) )
2326 firstRegion
= ®ion
;
2331 // Return the lateset data region by address
2332 const CacheBuilder::Region
* SharedCacheBuilder::lastDataRegion() const {
2333 const Region
* lastRegion
= nullptr;
2334 for (const Region
& region
: _dataRegions
) {
2335 if ( (lastRegion
== nullptr) || (region
.buffer
> lastRegion
->buffer
) )
2336 lastRegion
= ®ion
;
2340 static dyld_cache_patchable_location
makePatchLocation(size_t cacheOff
, dyld3::MachOAnalyzerSet::PointerMetaData pmd
, uint64_t addend
) {
2341 dyld_cache_patchable_location patch
;
2342 patch
.cacheOffset
= cacheOff
;
2343 patch
.high7
= pmd
.high8
>> 1;
2344 patch
.addend
= addend
;
2345 patch
.authenticated
= pmd
.authenticated
;
2346 patch
.usesAddressDiversity
= pmd
.usesAddrDiversity
;
2347 patch
.key
= pmd
.key
;
2348 patch
.discriminator
= pmd
.diversity
;
2349 // check for truncations
2350 assert(patch
.cacheOffset
== cacheOff
);
2351 assert(patch
.addend
== addend
);
2352 assert((patch
.high7
<< 1) == pmd
.high8
);
2356 void SharedCacheBuilder::buildImageArray(std::vector
<DyldSharedCache::FileAlias
>& aliases
)
2358 typedef dyld3::closure::ClosureBuilder::CachedDylibInfo CachedDylibInfo
;
2360 // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray()
2361 __block
std::vector
<CachedDylibInfo
> dylibInfos
;
2362 __block
std::unordered_map
<dyld3::closure::ImageNum
, const dyld3::MachOLoaded
*> imageNumToML
;
2363 DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
2364 cache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
2365 const dyld3::MachOLoaded
* ml
= (dyld3::MachOLoaded
*)mh
;
2366 if ( !_someDylibsUsedChainedFixups
&& ml
->hasChainedFixups() )
2367 _someDylibsUsedChainedFixups
= true;
2370 cache
->getIndexedImageEntry((uint32_t)dylibInfos
.size(), mtime
, inode
);
2371 CachedDylibInfo entry
;
2372 entry
.fileInfo
.fileContent
= mh
;
2373 entry
.fileInfo
.path
= installName
;
2374 entry
.fileInfo
.sliceOffset
= 0;
2375 entry
.fileInfo
.inode
= inode
;
2376 entry
.fileInfo
.mtime
= mtime
;
2377 dylibInfos
.push_back(entry
);
2378 imageNumToML
[(dyld3::closure::ImageNum
)(dylibInfos
.size())] = ml
;
2381 // Convert symlinks from STL to simple char pointers.
2382 std::vector
<dyld3::closure::ClosureBuilder::CachedDylibAlias
> dylibAliases
;
2383 dylibAliases
.reserve(aliases
.size());
2384 for (const auto& alias
: aliases
)
2385 dylibAliases
.push_back({ alias
.realPath
.c_str(), alias
.aliasPath
.c_str() });
2387 typedef dyld3::MachOAnalyzerSet::FixupTarget FixupTarget
;
2388 typedef dyld3::MachOAnalyzerSet::PointerMetaData PointerMetaData
;
2390 dyld3::closure::ClosureBuilder::DylibFixupHandler handler
= ^(const dyld3::MachOLoaded
* fixupIn
, uint64_t fixupLocRuntimeOffset
,
2391 PointerMetaData pmd
, const FixupTarget
& target
) {
2392 uint8_t* fixupLoc
= (uint8_t*)fixupIn
+ fixupLocRuntimeOffset
;
2393 uint32_t* fixupLoc32
= (uint32_t*)fixupLoc
;
2394 uint64_t* fixupLoc64
= (uint64_t*)fixupLoc
;
2395 uint64_t targetSymbolOffsetInCache
;
2396 switch ( target
.kind
) {
2397 case FixupTarget::Kind::rebase
:
2398 // rebasing already done in AdjustDylibSegments, but if input dylib uses chained fixups, target might not fit
2399 if ( _archLayout
->is64
) {
2400 if ( pmd
.authenticated
)
2401 _aslrTracker
.setAuthData(fixupLoc
, pmd
.diversity
, pmd
.usesAddrDiversity
, pmd
.key
);
2403 _aslrTracker
.setHigh8(fixupLoc
, pmd
.high8
);
2404 uint64_t targetVmAddr
;
2405 if ( _aslrTracker
.hasRebaseTarget64(fixupLoc
, &targetVmAddr
) )
2406 *fixupLoc64
= targetVmAddr
;
2408 *fixupLoc64
= (uint8_t*)target
.foundInImage
._mh
- _readExecuteRegion
.buffer
+ target
.offsetInImage
+ _readExecuteRegion
.unslidLoadAddress
;
2411 uint32_t targetVmAddr
;
2412 assert(_aslrTracker
.hasRebaseTarget32(fixupLoc
, &targetVmAddr
) && "32-bit archs always store target in side table");
2413 *fixupLoc32
= targetVmAddr
;
2416 case FixupTarget::Kind::bindAbsolute
:
2417 if ( _archLayout
->is64
)
2418 *fixupLoc64
= target
.offsetInImage
;
2420 *fixupLoc32
= (uint32_t)(target
.offsetInImage
);
2421 // don't record absolute targets for ASLR
2422 _aslrTracker
.remove(fixupLoc
);
2424 case FixupTarget::Kind::bindToImage
:
2425 targetSymbolOffsetInCache
= (uint8_t*)target
.foundInImage
._mh
- _readExecuteRegion
.buffer
+ target
.offsetInImage
- target
.addend
;
2426 if ( !target
.weakCoalesced
|| !_aslrTracker
.has(fixupLoc
) ) {
2427 // this handler is called a second time for weak_bind info, which we ignore when building cache
2428 _aslrTracker
.add(fixupLoc
);
2429 if ( _archLayout
->is64
) {
2431 _aslrTracker
.setHigh8(fixupLoc
, pmd
.high8
);
2432 if ( pmd
.authenticated
)
2433 _aslrTracker
.setAuthData(fixupLoc
, pmd
.diversity
, pmd
.usesAddrDiversity
, pmd
.key
);
2434 *fixupLoc64
= _archLayout
->sharedMemoryStart
+ targetSymbolOffsetInCache
+ target
.addend
;
2437 assert(targetSymbolOffsetInCache
< (_readOnlyRegion
.buffer
- _readExecuteRegion
.buffer
) && "offset not into TEXT or DATA of cache file");
2438 uint32_t targetVmAddr
;
2439 if ( _aslrTracker
.hasRebaseTarget32(fixupLoc
, &targetVmAddr
) )
2440 *fixupLoc32
= targetVmAddr
;
2442 *fixupLoc32
= (uint32_t)(_archLayout
->sharedMemoryStart
+ targetSymbolOffsetInCache
+ target
.addend
);
2445 _dylibToItsExports
[target
.foundInImage
._mh
].insert(targetSymbolOffsetInCache
);
2446 if ( target
.isWeakDef
)
2447 _dylibWeakExports
.insert({ target
.foundInImage
._mh
, targetSymbolOffsetInCache
});
2448 _exportsToUses
[targetSymbolOffsetInCache
].push_back(makePatchLocation(fixupLoc
- _readExecuteRegion
.buffer
, pmd
, target
.addend
));
2449 _exportsToName
[targetSymbolOffsetInCache
] = target
.foundSymbolName
;
2451 case FixupTarget::Kind::bindMissingSymbol
:
2452 // if there are missing symbols, makeDyldCacheImageArray() will error
2458 // build ImageArray for all dylibs in dyld cache
2459 dyld3::closure::PathOverrides pathOverrides
;
2460 dyld3::RootsChecker rootsChecker
;
2461 dyld3::closure::ClosureBuilder
cb(dyld3::closure::kFirstDyldCacheImageNum
, _fileSystem
, rootsChecker
, cache
, false, *_options
.archs
, pathOverrides
,
2462 dyld3::closure::ClosureBuilder::AtPath::none
, false, nullptr, _options
.platform
, handler
);
2463 dyld3::Array
<CachedDylibInfo
> dylibs(&dylibInfos
[0], dylibInfos
.size(), dylibInfos
.size());
2464 const dyld3::Array
<dyld3::closure::ClosureBuilder::CachedDylibAlias
> aliasesArray(dylibAliases
.data(), dylibAliases
.size(), dylibAliases
.size());
2465 _imageArray
= cb
.makeDyldCacheImageArray(dylibs
, aliasesArray
);
2466 if ( cb
.diagnostics().hasError() ) {
2467 _diagnostics
.error("%s", cb
.diagnostics().errorMessage().c_str());
2472 static bool operator==(const dyld_cache_patchable_location
& a
, const dyld_cache_patchable_location
& b
) {
2473 return a
.cacheOffset
== b
.cacheOffset
;
2476 void SharedCacheBuilder::addImageArray()
2478 // build trie of dylib paths
2479 __block
std::vector
<DylibIndexTrie::Entry
> dylibEntrys
;
2480 _imageArray
->forEachImage(^(const dyld3::closure::Image
* image
, bool& stop
) {
2481 dylibEntrys
.push_back(DylibIndexTrie::Entry(image
->path(), DylibIndex(image
->imageNum()-1)));
2482 image
->forEachAlias(^(const char *aliasPath
, bool &innerStop
) {
2483 dylibEntrys
.push_back(DylibIndexTrie::Entry(aliasPath
, DylibIndex(image
->imageNum()-1)));
2486 DylibIndexTrie
dylibsTrie(dylibEntrys
);
2487 std::vector
<uint8_t> trieBytes
;
2488 dylibsTrie
.emit(trieBytes
);
2489 while ( (trieBytes
.size() % 4) != 0 )
2490 trieBytes
.push_back(0);
2492 // build set of functions to never stub-eliminate because tools may need to override them
2493 std::unordered_set
<std::string
> alwaysGeneratePatch
;
2494 for (const char* const* p
=_s_neverStubEliminateSymbols
; *p
!= nullptr; ++p
)
2495 alwaysGeneratePatch
.insert(*p
);
2497 // Add the patches for the image array.
2498 __block
uint64_t numPatchImages
= _imageArray
->size();
2499 __block
uint64_t numPatchExports
= 0;
2500 __block
uint64_t numPatchLocations
= 0;
2501 __block
uint64_t numPatchExportNameBytes
= 0;
2503 auto needsPatch
= [&](bool dylibNeedsPatching
, const dyld3::MachOLoaded
* mh
,
2504 CacheOffset offset
) -> bool {
2505 if (dylibNeedsPatching
)
2507 if (_dylibWeakExports
.find({ mh
, offset
}) != _dylibWeakExports
.end())
2509 const std::string
& exportName
= _exportsToName
[offset
];
2510 return alwaysGeneratePatch
.find(exportName
) != alwaysGeneratePatch
.end();
2513 // First calculate how much space we need
2514 const DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
2515 cache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
2516 const dyld3::MachOLoaded
* ml
= (const dyld3::MachOLoaded
*)mh
;
2517 const std::set
<CacheOffset
>& dylibExports
= _dylibToItsExports
[ml
];
2519 // On a customer cache, only store patch locations for interposable dylibs and weak binding
2520 bool dylibNeedsPatching
= cache
->isOverridablePath(installName
);
2522 uint64_t numDylibExports
= 0;
2523 for (CacheOffset exportCacheOffset
: dylibExports
) {
2524 if (!needsPatch(dylibNeedsPatching
, ml
, exportCacheOffset
))
2526 std::vector
<dyld_cache_patchable_location
>& uses
= _exportsToUses
[exportCacheOffset
];
2527 uses
.erase(std::unique(uses
.begin(), uses
.end()), uses
.end());
2528 numPatchLocations
+= uses
.size();
2530 std::string exportName
= _exportsToName
[exportCacheOffset
];
2531 numPatchExportNameBytes
+= exportName
.size() + 1;
2533 numPatchExports
+= numDylibExports
;
2536 // Now reserve the space
2537 __block
std::vector
<dyld_cache_image_patches
> patchImages
;
2538 __block
std::vector
<dyld_cache_patchable_export
> patchExports
;
2539 __block
std::vector
<dyld_cache_patchable_location
> patchLocations
;
2540 __block
std::vector
<char> patchExportNames
;
2542 patchImages
.reserve(numPatchImages
);
2543 patchExports
.reserve(numPatchExports
);
2544 patchLocations
.reserve(numPatchLocations
);
2545 patchExportNames
.reserve(numPatchExportNameBytes
);
2547 // And now fill it with the patch data
2548 cache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
2549 const dyld3::MachOLoaded
* ml
= (const dyld3::MachOLoaded
*)mh
;
2550 const std::set
<CacheOffset
>& dylibExports
= _dylibToItsExports
[ml
];
2552 // On a customer cache, only store patch locations for interposable dylibs and weak binding
2553 bool dylibNeedsPatching
= cache
->isOverridablePath(installName
);
2555 // Add the patch image which points in to the exports
2556 dyld_cache_image_patches patchImage
;
2557 patchImage
.patchExportsStartIndex
= (uint32_t)patchExports
.size();
2558 patchImage
.patchExportsCount
= 0;
2560 // Then add each export which points to a list of locations and a name
2561 for (CacheOffset exportCacheOffset
: dylibExports
) {
2562 if (!needsPatch(dylibNeedsPatching
, ml
, exportCacheOffset
))
2564 ++patchImage
.patchExportsCount
;
2565 std::vector
<dyld_cache_patchable_location
>& uses
= _exportsToUses
[exportCacheOffset
];
2567 dyld_cache_patchable_export cacheExport
;
2568 cacheExport
.cacheOffsetOfImpl
= (uint32_t)exportCacheOffset
;
2569 cacheExport
.patchLocationsStartIndex
= (uint32_t)patchLocations
.size();
2570 cacheExport
.patchLocationsCount
= (uint32_t)uses
.size();
2571 cacheExport
.exportNameOffset
= (uint32_t)patchExportNames
.size();
2572 patchExports
.push_back(cacheExport
);
2574 // Now add the list of locations.
2575 patchLocations
.insert(patchLocations
.end(), uses
.begin(), uses
.end());
2577 // And add the export name
2578 const std::string
& exportName
= _exportsToName
[exportCacheOffset
];
2579 patchExportNames
.insert(patchExportNames
.end(), &exportName
[0], &exportName
[0] + exportName
.size() + 1);
2581 patchImages
.push_back(patchImage
);
2584 while ( (patchExportNames
.size() % 4) != 0 )
2585 patchExportNames
.push_back('\0');
2587 uint64_t patchInfoSize
= sizeof(dyld_cache_patch_info
);
2588 patchInfoSize
+= sizeof(dyld_cache_image_patches
) * patchImages
.size();
2589 patchInfoSize
+= sizeof(dyld_cache_patchable_export
) * patchExports
.size();
2590 patchInfoSize
+= sizeof(dyld_cache_patchable_location
) * patchLocations
.size();
2591 patchInfoSize
+= patchExportNames
.size();
2594 uint64_t imageArraySize
= _imageArray
->size();
2595 size_t freeSpace
= _readOnlyRegion
.bufferSize
- _readOnlyRegion
.sizeInUse
;
2596 if ( (imageArraySize
+trieBytes
.size()+patchInfoSize
) > freeSpace
) {
2597 _diagnostics
.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, patch size=%lluKB, free space=%ldMB)",
2598 _allocatedBufferSize
/1024/1024, imageArraySize
/1024/1024, trieBytes
.size()/1024, patchInfoSize
/1024, freeSpace
/1024/1024);
2602 // copy into cache and update header
2603 DyldSharedCache
* dyldCache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
2604 dyldCache
->header
.dylibsImageArrayAddr
= _readOnlyRegion
.unslidLoadAddress
+ _readOnlyRegion
.sizeInUse
;
2605 dyldCache
->header
.dylibsImageArraySize
= imageArraySize
;
2606 dyldCache
->header
.dylibsTrieAddr
= dyldCache
->header
.dylibsImageArrayAddr
+ imageArraySize
;
2607 dyldCache
->header
.dylibsTrieSize
= trieBytes
.size();
2608 ::memcpy(_readOnlyRegion
.buffer
+ _readOnlyRegion
.sizeInUse
, _imageArray
, imageArraySize
);
2609 ::memcpy(_readOnlyRegion
.buffer
+ _readOnlyRegion
.sizeInUse
+ imageArraySize
, &trieBytes
[0], trieBytes
.size());
2611 // Also write out the patch info
2612 dyldCache
->header
.patchInfoAddr
= dyldCache
->header
.dylibsTrieAddr
+ dyldCache
->header
.dylibsTrieSize
;
2613 dyldCache
->header
.patchInfoSize
= patchInfoSize
;
2614 dyld_cache_patch_info patchInfo
;
2615 patchInfo
.patchTableArrayAddr
= dyldCache
->header
.patchInfoAddr
+ sizeof(dyld_cache_patch_info
);
2616 patchInfo
.patchTableArrayCount
= patchImages
.size();
2617 patchInfo
.patchExportArrayAddr
= patchInfo
.patchTableArrayAddr
+ (patchInfo
.patchTableArrayCount
* sizeof(dyld_cache_image_patches
));
2618 patchInfo
.patchExportArrayCount
= patchExports
.size();
2619 patchInfo
.patchLocationArrayAddr
= patchInfo
.patchExportArrayAddr
+ (patchInfo
.patchExportArrayCount
* sizeof(dyld_cache_patchable_export
));
2620 patchInfo
.patchLocationArrayCount
= patchLocations
.size();
2621 patchInfo
.patchExportNamesAddr
= patchInfo
.patchLocationArrayAddr
+ (patchInfo
.patchLocationArrayCount
* sizeof(dyld_cache_patchable_location
));
2622 patchInfo
.patchExportNamesSize
= patchExportNames
.size();
2623 ::memcpy(_readOnlyRegion
.buffer
+ dyldCache
->header
.patchInfoAddr
- _readOnlyRegion
.unslidLoadAddress
,
2624 &patchInfo
, sizeof(dyld_cache_patch_info
));
2625 ::memcpy(_readOnlyRegion
.buffer
+ patchInfo
.patchTableArrayAddr
- _readOnlyRegion
.unslidLoadAddress
,
2626 &patchImages
[0], sizeof(patchImages
[0]) * patchImages
.size());
2627 ::memcpy(_readOnlyRegion
.buffer
+ patchInfo
.patchExportArrayAddr
- _readOnlyRegion
.unslidLoadAddress
,
2628 &patchExports
[0], sizeof(patchExports
[0]) * patchExports
.size());
2629 ::memcpy(_readOnlyRegion
.buffer
+ patchInfo
.patchLocationArrayAddr
- _readOnlyRegion
.unslidLoadAddress
,
2630 &patchLocations
[0], sizeof(patchLocations
[0]) * patchLocations
.size());
2631 ::memcpy(_readOnlyRegion
.buffer
+ patchInfo
.patchExportNamesAddr
- _readOnlyRegion
.unslidLoadAddress
,
2632 &patchExportNames
[0], patchExportNames
.size());
2634 _readOnlyRegion
.sizeInUse
+= align(imageArraySize
+trieBytes
.size()+patchInfoSize
,14);
2636 // Free the underlying image array buffer
2637 _imageArray
->deallocate();
2638 _imageArray
= nullptr;
2641 void SharedCacheBuilder::addOtherImageArray(const std::vector
<LoadedMachO
>& otherDylibsAndBundles
, std::vector
<const LoadedMachO
*>& overflowDylibs
)
2643 DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
2644 dyld3::closure::PathOverrides pathOverrides
;
2645 dyld3::closure::FileSystemNull nullFileSystem
;
2646 dyld3::RootsChecker rootsChecker
;
2647 dyld3::closure::ClosureBuilder
cb(dyld3::closure::kFirstOtherOSImageNum
, nullFileSystem
, rootsChecker
, cache
, false, *_options
.archs
, pathOverrides
,
2648 dyld3::closure::ClosureBuilder::AtPath::none
, false, nullptr, _options
.platform
);
2650 // make ImageArray for other dylibs and bundles
2651 STACK_ALLOC_ARRAY(dyld3::closure::LoadedFileInfo
, others
, otherDylibsAndBundles
.size() + overflowDylibs
.size());
2652 for (const LoadedMachO
& other
: otherDylibsAndBundles
) {
2653 if ( !contains(other
.loadedFileInfo
.path
, "staged_system_apps/") )
2654 others
.push_back(other
.loadedFileInfo
);
2657 for (const LoadedMachO
* dylib
: overflowDylibs
) {
2658 if (dylib
->mappedFile
.mh
->canHavePrecomputedDlopenClosure(dylib
->mappedFile
.runtimePath
.c_str(), ^(const char*) {}) )
2659 others
.push_back(dylib
->loadedFileInfo
);
2662 // Sort the others array by name so that it is deterministic
2663 std::sort(others
.begin(), others
.end(),
2664 [](const dyld3::closure::LoadedFileInfo
& a
, const dyld3::closure::LoadedFileInfo
& b
) {
2665 // Sort mac before iOSMac
2666 bool isIOSMacA
= strncmp(a
.path
, "/System/iOSSupport/", 19) == 0;
2667 bool isIOSMacB
= strncmp(b
.path
, "/System/iOSSupport/", 19) == 0;
2668 if (isIOSMacA
!= isIOSMacB
)
2670 return strcmp(a
.path
, b
.path
) < 0;
2673 const dyld3::closure::ImageArray
* otherImageArray
= cb
.makeOtherDylibsImageArray(others
, (uint32_t)_sortedDylibs
.size());
2675 // build trie of paths
2676 __block
std::vector
<DylibIndexTrie::Entry
> otherEntrys
;
2677 otherImageArray
->forEachImage(^(const dyld3::closure::Image
* image
, bool& stop
) {
2678 if ( !image
->isInvalid() )
2679 otherEntrys
.push_back(DylibIndexTrie::Entry(image
->path(), DylibIndex(image
->imageNum())));
2681 DylibIndexTrie
dylibsTrie(otherEntrys
);
2682 std::vector
<uint8_t> trieBytes
;
2683 dylibsTrie
.emit(trieBytes
);
2684 while ( (trieBytes
.size() % 4) != 0 )
2685 trieBytes
.push_back(0);
2688 uint64_t imageArraySize
= otherImageArray
->size();
2689 size_t freeSpace
= _readOnlyRegion
.bufferSize
- _readOnlyRegion
.sizeInUse
;
2690 if ( imageArraySize
+trieBytes
.size() > freeSpace
) {
2691 _diagnostics
.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
2692 _allocatedBufferSize
/1024/1024, imageArraySize
/1024/1024, trieBytes
.size()/1024, freeSpace
/1024/1024);
2696 // copy into cache and update header
2697 DyldSharedCache
* dyldCache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
2698 dyldCache
->header
.otherImageArrayAddr
= _readOnlyRegion
.unslidLoadAddress
+ _readOnlyRegion
.sizeInUse
;
2699 dyldCache
->header
.otherImageArraySize
= imageArraySize
;
2700 dyldCache
->header
.otherTrieAddr
= dyldCache
->header
.otherImageArrayAddr
+ imageArraySize
;
2701 dyldCache
->header
.otherTrieSize
= trieBytes
.size();
2702 ::memcpy(_readOnlyRegion
.buffer
+ _readOnlyRegion
.sizeInUse
, otherImageArray
, imageArraySize
);
2703 ::memcpy(_readOnlyRegion
.buffer
+ _readOnlyRegion
.sizeInUse
+ imageArraySize
, &trieBytes
[0], trieBytes
.size());
2704 _readOnlyRegion
.sizeInUse
+= align(imageArraySize
+trieBytes
.size(),14);
2706 // Free the underlying buffer
2707 otherImageArray
->deallocate();
2711 void SharedCacheBuilder::addClosures(const std::vector
<LoadedMachO
>& osExecutables
)
2713 const DyldSharedCache
* dyldCache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
2715 __block
std::vector
<Diagnostics
> osExecutablesDiags
;
2716 __block
std::vector
<const dyld3::closure::LaunchClosure
*> osExecutablesClosures
;
2717 osExecutablesDiags
.resize(osExecutables
.size());
2718 osExecutablesClosures
.resize(osExecutables
.size());
2720 dispatch_apply(osExecutables
.size(), DISPATCH_APPLY_AUTO
, ^(size_t index
) {
2721 const LoadedMachO
& loadedMachO
= osExecutables
[index
];
2722 // don't pre-build closures for staged apps into dyld cache, since they won't run from that location
2723 if ( startsWith(loadedMachO
.mappedFile
.runtimePath
, "/private/var/staged_system_apps/") ) {
2727 // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same
2728 // at runtime as when the shared cache processed it. We must have a code signature to record this information
2729 uint32_t codeSigFileOffset
;
2730 uint32_t codeSigSize
;
2731 if ( !loadedMachO
.mappedFile
.mh
->hasCodeSignature(codeSigFileOffset
, codeSigSize
) ) {
2735 dyld3::closure::PathOverrides pathOverrides
;
2736 dyld3::RootsChecker rootsChecker
;
2737 dyld3::closure::ClosureBuilder
builder(dyld3::closure::kFirstLaunchClosureImageNum
, _fileSystem
, rootsChecker
, dyldCache
, false, *_options
.archs
, pathOverrides
,
2738 dyld3::closure::ClosureBuilder::AtPath::all
, false, nullptr, _options
.platform
, nullptr);
2739 bool issetuid
= false;
2740 if ( this->_options
.platform
== dyld3::Platform::macOS
|| dyld3::MachOFile::isSimulatorPlatform(this->_options
.platform
) )
2741 _fileSystem
.fileExists(loadedMachO
.loadedFileInfo
.path
, nullptr, nullptr, &issetuid
, nullptr);
2742 const dyld3::closure::LaunchClosure
* mainClosure
= builder
.makeLaunchClosure(loadedMachO
.loadedFileInfo
, issetuid
);
2743 if ( builder
.diagnostics().hasError() ) {
2744 osExecutablesDiags
[index
].error("%s", builder
.diagnostics().errorMessage().c_str());
2747 assert(mainClosure
!= nullptr);
2748 osExecutablesClosures
[index
] = mainClosure
;
2752 std::map
<std::string
, const dyld3::closure::LaunchClosure
*> closures
;
2753 for (uint64_t i
= 0, e
= osExecutables
.size(); i
!= e
; ++i
) {
2754 const LoadedMachO
& loadedMachO
= osExecutables
[i
];
2755 const Diagnostics
& diag
= osExecutablesDiags
[i
];
2756 if (diag
.hasError()) {
2757 if ( _options
.verbose
) {
2758 _diagnostics
.warning("building closure for '%s': %s", loadedMachO
.mappedFile
.runtimePath
.c_str(), diag
.errorMessage().c_str());
2759 for (const std::string
& warn
: diag
.warnings() )
2760 _diagnostics
.warning("%s", warn
.c_str());
2762 if ( loadedMachO
.inputFile
&& (loadedMachO
.inputFile
->mustBeIncluded()) ) {
2763 loadedMachO
.inputFile
->diag
.error("%s", diag
.errorMessage().c_str());
2766 // Note, a closure could be null here if it has a path we skip.
2767 if (osExecutablesClosures
[i
] != nullptr)
2768 closures
[loadedMachO
.mappedFile
.runtimePath
] = osExecutablesClosures
[i
];
2772 osExecutablesDiags
.clear();
2773 osExecutablesClosures
.clear();
2775 // preflight space needed
2776 size_t closuresSpace
= 0;
2777 for (const auto& entry
: closures
) {
2778 closuresSpace
+= entry
.second
->size();
2780 size_t freeSpace
= _readOnlyRegion
.bufferSize
- _readOnlyRegion
.sizeInUse
;
2781 if ( closuresSpace
> freeSpace
) {
2782 _diagnostics
.error("cache buffer too small to hold all closures (buffer size=%lldMB, closures size=%ldMB, free space=%ldMB)",
2783 _allocatedBufferSize
/1024/1024, closuresSpace
/1024/1024, freeSpace
/1024/1024);
2786 DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
2787 cache
->header
.progClosuresAddr
= _readOnlyRegion
.unslidLoadAddress
+ _readOnlyRegion
.sizeInUse
;
2788 uint8_t* closuresBase
= _readOnlyRegion
.buffer
+ _readOnlyRegion
.sizeInUse
;
2789 std::vector
<DylibIndexTrie::Entry
> closureEntrys
;
2790 uint32_t currentClosureOffset
= 0;
2791 for (const auto& entry
: closures
) {
2792 const dyld3::closure::LaunchClosure
* closure
= entry
.second
;
2793 closureEntrys
.push_back(DylibIndexTrie::Entry(entry
.first
, DylibIndex(currentClosureOffset
)));
2794 size_t size
= closure
->size();
2795 assert((size
% 4) == 0);
2796 memcpy(closuresBase
+currentClosureOffset
, closure
, size
);
2797 currentClosureOffset
+= size
;
2799 closure
->deallocate();
2801 cache
->header
.progClosuresSize
= currentClosureOffset
;
2802 _readOnlyRegion
.sizeInUse
+= currentClosureOffset
;
2803 freeSpace
= _readOnlyRegion
.bufferSize
- _readOnlyRegion
.sizeInUse
;
2804 // build trie of indexes into closures list
2805 DylibIndexTrie
closureTrie(closureEntrys
);
2806 std::vector
<uint8_t> trieBytes
;
2807 closureTrie
.emit(trieBytes
);
2808 while ( (trieBytes
.size() % 8) != 0 )
2809 trieBytes
.push_back(0);
2810 if ( trieBytes
.size() > freeSpace
) {
2811 _diagnostics
.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)",
2812 _allocatedBufferSize
/1024/1024, trieBytes
.size()/1024/1024, freeSpace
/1024/1024);
2815 memcpy(_readOnlyRegion
.buffer
+ _readOnlyRegion
.sizeInUse
, &trieBytes
[0], trieBytes
.size());
2816 cache
->header
.progClosuresTrieAddr
= _readOnlyRegion
.unslidLoadAddress
+ _readOnlyRegion
.sizeInUse
;
2817 cache
->header
.progClosuresTrieSize
= trieBytes
.size();
2818 _readOnlyRegion
.sizeInUse
+= trieBytes
.size();
2819 _readOnlyRegion
.sizeInUse
= align(_readOnlyRegion
.sizeInUse
, 14);
2822 void SharedCacheBuilder::emitContantObjects() {
2823 if ( _coalescedText
.cfStrings
.bufferSize
== 0 )
2826 assert(_coalescedText
.cfStrings
.isaInstallName
!= nullptr);
2827 DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
2828 __block
uint64_t targetSymbolOffsetInCache
= 0;
2829 __block
const dyld3::MachOAnalyzer
* targetSymbolMA
= nullptr;
2830 __block
const dyld3::MachOAnalyzer
* libdyldMA
= nullptr;
2831 cache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
2832 const dyld3::MachOAnalyzer
* ma
= (const dyld3::MachOAnalyzer
*)mh
;
2834 if ( strcmp(installName
, "/usr/lib/system/libdyld.dylib") == 0 ) {
2838 if ( targetSymbolOffsetInCache
!= 0 )
2840 if ( strcmp(installName
, _coalescedText
.cfStrings
.isaInstallName
) != 0 )
2842 dyld3::MachOAnalyzer::FoundSymbol foundInfo
;
2843 bool foundSymbol
= ma
->findExportedSymbol(_diagnostics
, _coalescedText
.cfStrings
.isaClassName
,
2844 false, foundInfo
, nullptr);
2845 if ( foundSymbol
) {
2846 targetSymbolOffsetInCache
= (uint8_t*)ma
- _readExecuteRegion
.buffer
+ foundInfo
.value
;
2847 targetSymbolMA
= ma
;
2850 if ( targetSymbolOffsetInCache
== 0 ) {
2851 _diagnostics
.error("Could not find export of '%s' in '%s'", _coalescedText
.cfStrings
.isaClassName
,
2852 _coalescedText
.cfStrings
.isaInstallName
);
2855 if ( libdyldMA
== nullptr ) {
2856 _diagnostics
.error("Could not libdyld.dylib in shared cache");
2860 // If all binds to this symbol were via CF constants, then we'll never have seen the ISA patch export
2861 // os add it now just in case
2862 _dylibToItsExports
[targetSymbolMA
].insert(targetSymbolOffsetInCache
);
2863 _exportsToName
[targetSymbolOffsetInCache
] = _coalescedText
.cfStrings
.isaClassName
;
2865 // CFString's have so far just been memcpy'ed from the source dylib to the shared cache.
2866 // We now need to rewrite their ISAs to be rebases to the ___CFConstantStringClassReference class
2867 const uint64_t cfStringAtomSize
= (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize
;
2868 assert( (_coalescedText
.cfStrings
.bufferSize
% cfStringAtomSize
) == 0);
2869 for (uint64_t bufferOffset
= 0; bufferOffset
!= _coalescedText
.cfStrings
.bufferSize
; bufferOffset
+= cfStringAtomSize
) {
2870 uint8_t* atomBuffer
= _coalescedText
.cfStrings
.bufferAddr
+ bufferOffset
;
2871 // The ISA fixup is at an offset of 0 in to the atom
2872 uint8_t* fixupLoc
= atomBuffer
;
2873 // We purposefully want to remove the pointer authentication from the ISA so
2874 // just use an empty pointer metadata
2875 dyld3::Loader::PointerMetaData pmd
;
2876 uint64_t addend
= 0;
2877 _exportsToUses
[targetSymbolOffsetInCache
].push_back(makePatchLocation(fixupLoc
- _readExecuteRegion
.buffer
, pmd
, addend
));
2878 *(uint64_t*)fixupLoc
= _archLayout
->sharedMemoryStart
+ targetSymbolOffsetInCache
;
2879 _aslrTracker
.add(fixupLoc
);
2882 // Set the ranges in the libdyld in the shared cache. At runtime we can use these to quickly check if a given address
2883 // is a valid constant
2884 typedef std::pair
<const uint8_t*, const uint8_t*> ObjCConstantRange
;
2885 std::pair
<const void*, uint64_t> sharedCacheRanges
= cache
->getObjCConstantRange();
2886 uint64_t numRanges
= sharedCacheRanges
.second
/ sizeof(ObjCConstantRange
);
2887 dyld3::Array
<ObjCConstantRange
> rangeArray((ObjCConstantRange
*)sharedCacheRanges
.first
, numRanges
, numRanges
);
2889 if ( numRanges
> dyld_objc_string_kind
) {
2890 rangeArray
[dyld_objc_string_kind
].first
= (const uint8_t*)_coalescedText
.cfStrings
.bufferVMAddr
;
2891 rangeArray
[dyld_objc_string_kind
].second
= rangeArray
[dyld_objc_string_kind
].first
+ _coalescedText
.cfStrings
.bufferSize
;
2892 _aslrTracker
.add(&rangeArray
[dyld_objc_string_kind
].first
);
2893 _aslrTracker
.add(&rangeArray
[dyld_objc_string_kind
].second
);
2896 // Update the __SHARED_CACHE range in libdyld to contain the cf/objc constants
2897 libdyldMA
->forEachLoadCommand(_diagnostics
, ^(const load_command
* cmd
, bool& stop
) {
2898 // We don't handle 32-bit as this is only needed for pointer authentication
2899 assert(cmd
->cmd
!= LC_SEGMENT
);
2900 if ( cmd
->cmd
== LC_SEGMENT_64
) {
2901 segment_command_64
* seg
= (segment_command_64
*)cmd
;
2902 if ( strcmp(seg
->segname
, "__SHARED_CACHE") == 0 ) {
2903 // Update the range of this segment, and any sections inside
2904 seg
->vmaddr
= _coalescedText
.cfStrings
.bufferVMAddr
;
2905 seg
->vmsize
= _coalescedText
.cfStrings
.bufferSize
;
2906 seg
->fileoff
= _coalescedText
.cfStrings
.cacheFileOffset
;
2907 seg
->fileoff
= _coalescedText
.cfStrings
.bufferSize
;
2908 section_64
* const sectionsStart
= (section_64
*)((char*)seg
+ sizeof(struct segment_command_64
));
2909 section_64
* const sectionsEnd
= §ionsStart
[seg
->nsects
];
2910 for (section_64
* sect
=sectionsStart
; sect
< sectionsEnd
; ++sect
) {
2911 if ( !strcmp(sect
->sectname
, "__cfstring") ) {
2912 sect
->addr
= _coalescedText
.cfStrings
.bufferVMAddr
;
2913 sect
->size
= _coalescedText
.cfStrings
.bufferSize
;
2914 sect
->offset
= (uint32_t)_coalescedText
.cfStrings
.cacheFileOffset
;
2924 bool SharedCacheBuilder::writeCache(void (^cacheSizeCallback
)(uint64_t size
), bool (^copyCallback
)(const uint8_t* src
, uint64_t size
, uint64_t dstOffset
))
2926 const dyld_cache_header
* cacheHeader
= (dyld_cache_header
*)_readExecuteRegion
.buffer
;
2927 const dyld_cache_mapping_info
* mappings
= (dyld_cache_mapping_info
*)(_readExecuteRegion
.buffer
+ cacheHeader
->mappingOffset
);
2928 const uint32_t mappingsCount
= cacheHeader
->mappingCount
;
2929 // Check the sizes of all the regions are correct
2930 assert(_readExecuteRegion
.sizeInUse
== mappings
[0].size
);
2931 for (uint32_t i
= 0; i
!= _dataRegions
.size(); ++i
) {
2932 assert(_dataRegions
[i
].sizeInUse
== mappings
[i
+ 1].size
);
2934 assert(_readOnlyRegion
.sizeInUse
== mappings
[mappingsCount
- 1].size
);
2936 // Check the file offsets of all the regions are correct
2937 assert(_readExecuteRegion
.cacheFileOffset
== mappings
[0].fileOffset
);
2938 for (uint32_t i
= 0; i
!= _dataRegions
.size(); ++i
) {
2939 assert(_dataRegions
[i
].cacheFileOffset
== mappings
[i
+ 1].fileOffset
);
2941 assert(_readOnlyRegion
.cacheFileOffset
== mappings
[mappingsCount
- 1].fileOffset
);
2942 assert(_codeSignatureRegion
.sizeInUse
== cacheHeader
->codeSignatureSize
);
2943 assert(cacheHeader
->codeSignatureOffset
== _readOnlyRegion
.cacheFileOffset
+_readOnlyRegion
.sizeInUse
+_localSymbolsRegion
.sizeInUse
);
2945 // Make sure the slidable mappings have the same ranges as the original mappings
2946 const dyld_cache_mapping_and_slide_info
* slidableMappings
= (dyld_cache_mapping_and_slide_info
*)(_readExecuteRegion
.buffer
+ cacheHeader
->mappingWithSlideOffset
);
2947 assert(cacheHeader
->mappingCount
== cacheHeader
->mappingWithSlideCount
);
2948 for (uint32_t i
= 0; i
!= cacheHeader
->mappingCount
; ++i
) {
2949 assert(mappings
[i
].address
== slidableMappings
[i
].address
);
2950 assert(mappings
[i
].size
== slidableMappings
[i
].size
);
2951 assert(mappings
[i
].fileOffset
== slidableMappings
[i
].fileOffset
);
2952 assert(mappings
[i
].maxProt
== slidableMappings
[i
].maxProt
);
2953 assert(mappings
[i
].initProt
== slidableMappings
[i
].initProt
);
2956 // Now that we know everything is correct, actually copy the data
2957 cacheSizeCallback(_readExecuteRegion
.sizeInUse
+dataRegionsSizeInUse()+_readOnlyRegion
.sizeInUse
+_localSymbolsRegion
.sizeInUse
+_codeSignatureRegion
.sizeInUse
);
2958 bool fullyWritten
= copyCallback(_readExecuteRegion
.buffer
, _readExecuteRegion
.sizeInUse
, mappings
[0].fileOffset
);
2959 for (uint32_t i
= 0; i
!= _dataRegions
.size(); ++i
) {
2960 fullyWritten
&= copyCallback(_dataRegions
[i
].buffer
, _dataRegions
[i
].sizeInUse
, mappings
[i
+ 1].fileOffset
);
2962 fullyWritten
&= copyCallback(_readOnlyRegion
.buffer
, _readOnlyRegion
.sizeInUse
, mappings
[cacheHeader
->mappingCount
- 1].fileOffset
);
2963 if ( _localSymbolsRegion
.sizeInUse
!= 0 ) {
2964 assert(cacheHeader
->localSymbolsOffset
== mappings
[cacheHeader
->mappingCount
- 1].fileOffset
+_readOnlyRegion
.sizeInUse
);
2965 fullyWritten
&= copyCallback(_localSymbolsRegion
.buffer
, _localSymbolsRegion
.sizeInUse
, cacheHeader
->localSymbolsOffset
);
2967 fullyWritten
&= copyCallback(_codeSignatureRegion
.buffer
, _codeSignatureRegion
.sizeInUse
, cacheHeader
->codeSignatureOffset
);
2968 return fullyWritten
;
2972 void SharedCacheBuilder::writeFile(const std::string
& path
)
2974 std::string pathTemplate
= path
+ "-XXXXXX";
2975 size_t templateLen
= strlen(pathTemplate
.c_str())+2;
2976 BLOCK_ACCCESSIBLE_ARRAY(char, pathTemplateSpace
, templateLen
);
2977 strlcpy(pathTemplateSpace
, pathTemplate
.c_str(), templateLen
);
2978 int fd
= mkstemp(pathTemplateSpace
);
2980 auto cacheSizeCallback
= ^(uint64_t size
) {
2981 // set final cache file size (may help defragment file)
2982 ::ftruncate(fd
, size
);
2984 auto copyCallback
= ^(const uint8_t* src
, uint64_t size
, uint64_t dstOffset
) {
2985 uint64_t writtenSize
= pwrite(fd
, src
, size
, dstOffset
);
2986 return writtenSize
== size
;
2988 // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
2989 char tempPath
[MAXPATHLEN
];
2990 if ( ::fcntl(fd
, F_GETPATH
, tempPath
) == 0 ) {
2991 size_t tempPathLen
= strlen(tempPath
);
2992 if ( tempPathLen
> 7 )
2993 tempPath
[tempPathLen
-7] = '\0'; // remove trailing -xxxxxx
2994 if ( path
!= tempPath
) {
2995 _diagnostics
.error("output file path changed from: '%s' to: '%s'", path
.c_str(), tempPath
);
3001 _diagnostics
.error("unable to fcntl(fd, F_GETPATH) on output file");
3005 bool fullyWritten
= writeCache(cacheSizeCallback
, copyCallback
);
3006 if ( fullyWritten
) {
3007 ::fchmod(fd
, S_IRUSR
|S_IRGRP
|S_IROTH
); // mkstemp() makes file "rw-------", switch it to "r--r--r--"
3008 // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
3009 // For MRM bringup, dyld installs symlinks from:
3010 // dyld_shared_cache_x86_64 -> ../../../../System/Library/dyld/dyld_shared_cache_x86_64
3011 // dyld_shared_cache_x86_64h -> ../../../../System/Library/dyld/dyld_shared_cache_x86_64h
3012 // We don't want to follow that symlink when we install the cache, but instead write over it
3013 auto lastSlash
= path
.find_last_of("/");
3014 if ( lastSlash
!= std::string::npos
) {
3015 std::string directoryPath
= path
.substr(0, lastSlash
);
3017 char resolvedPath
[PATH_MAX
];
3018 ::realpath(directoryPath
.c_str(), resolvedPath
);
3019 // Note: if the target cache file does not already exist, realpath() will return NULL, but still fill in the path buffer
3020 if ( directoryPath
!= resolvedPath
) {
3021 _diagnostics
.error("output directory file path changed from: '%s' to: '%s'", directoryPath
.c_str(), resolvedPath
);
3025 if ( ::rename(pathTemplateSpace
, path
.c_str()) == 0) {
3029 _diagnostics
.error("could not rename file '%s' to: '%s'", pathTemplateSpace
, path
.c_str());
3033 _diagnostics
.error("could not write file %s", pathTemplateSpace
);
3036 ::unlink(pathTemplateSpace
);
3039 _diagnostics
.error("could not open file %s", pathTemplateSpace
);
3043 void SharedCacheBuilder::writeBuffer(uint8_t*& buffer
, uint64_t& bufferSize
) {
3044 auto cacheSizeCallback
= ^(uint64_t size
) {
3045 buffer
= (uint8_t*)malloc(size
);
3048 auto copyCallback
= ^(const uint8_t* src
, uint64_t size
, uint64_t dstOffset
) {
3049 memcpy(buffer
+ dstOffset
, src
, size
);
3052 bool fullyWritten
= writeCache(cacheSizeCallback
, copyCallback
);
3053 assert(fullyWritten
);
3056 void SharedCacheBuilder::writeMapFile(const std::string
& path
)
3058 std::string mapContent
= getMapFileBuffer();
3059 safeSave(mapContent
.c_str(), mapContent
.size(), path
);
3062 std::string
SharedCacheBuilder::getMapFileBuffer() const
3064 const DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
3065 return cache
->mapFile();
3068 std::string
SharedCacheBuilder::getMapFileJSONBuffer(const std::string
& cacheDisposition
) const
3070 const DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
3071 return cache
->generateJSONMap(cacheDisposition
.c_str());
3074 void SharedCacheBuilder::markPaddingInaccessible()
3076 // region between RX and RW
3077 uint8_t* startPad1
= _readExecuteRegion
.buffer
+_readExecuteRegion
.sizeInUse
;
3078 uint8_t* endPad1
= firstDataRegion()->buffer
;
3079 ::vm_protect(mach_task_self(), (vm_address_t
)startPad1
, endPad1
-startPad1
, false, 0);
3081 // region between RW and RO
3082 const Region
* lastRegion
= lastDataRegion();
3083 uint8_t* startPad2
= lastRegion
->buffer
+lastRegion
->sizeInUse
;
3084 uint8_t* endPad2
= _readOnlyRegion
.buffer
;
3085 ::vm_protect(mach_task_self(), (vm_address_t
)startPad2
, endPad2
-startPad2
, false, 0);
3089 void SharedCacheBuilder::forEachCacheDylib(void (^callback
)(const std::string
& path
)) {
3090 for (const DylibInfo
& dylibInfo
: _sortedDylibs
)
3091 callback(dylibInfo
.dylibID
);
3095 void SharedCacheBuilder::forEachCacheSymlink(void (^callback
)(const std::string
& path
))
3097 const DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
3098 const dyld3::closure::ImageArray
* images
= cache
->cachedDylibsImageArray();
3099 if ( images
== nullptr )
3102 // Aliases we folded in to the cache are in the cache dylib closures
3103 images
->forEachImage(^(const dyld3::closure::Image
*image
, bool &stop
) {
3104 image
->forEachAlias(^(const char *aliasPath
, bool &stop
) {
3105 callback(aliasPath
);
3111 uint64_t SharedCacheBuilder::pathHash(const char* path
)
3114 for (const char* s
=path
; *s
!= '\0'; ++s
)
3120 void SharedCacheBuilder::findDylibAndSegment(const void* contentPtr
, std::string
& foundDylibName
, std::string
& foundSegName
)
3122 foundDylibName
= "???";
3123 foundSegName
= "???";
3124 uint64_t unslidVmAddr
= ((uint8_t*)contentPtr
- _readExecuteRegion
.buffer
) + _readExecuteRegion
.unslidLoadAddress
;
3125 const DyldSharedCache
* cache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
3126 cache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
3127 ((dyld3::MachOLoaded
*)mh
)->forEachSegment(^(const dyld3::MachOFile::SegmentInfo
& info
, bool &stop
) {
3128 if ( (unslidVmAddr
>= info
.vmAddr
) && (unslidVmAddr
< (info
.vmAddr
+info
.vmSize
)) ) {
3129 foundDylibName
= installName
;
3130 foundSegName
= info
.segName
;
3138 void SharedCacheBuilder::fipsSign()
3140 // find libcorecrypto.dylib in cache being built
3141 DyldSharedCache
* dyldCache
= (DyldSharedCache
*)_readExecuteRegion
.buffer
;
3142 __block
const dyld3::MachOLoaded
* ml
= nullptr;
3143 dyldCache
->forEachImage(^(const mach_header
* mh
, const char* installName
) {
3144 if ( strcmp(installName
, "/usr/lib/system/libcorecrypto.dylib") == 0 )
3145 ml
= (dyld3::MachOLoaded
*)mh
;
3147 if ( ml
== nullptr ) {
3148 _diagnostics
.warning("Could not find libcorecrypto.dylib, skipping FIPS sealing");
3152 // find location in libcorecrypto.dylib to store hash of __text section
3153 uint64_t hashStoreSize
;
3154 const void* hashStoreLocation
= ml
->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize
);
3155 if ( hashStoreLocation
== nullptr ) {
3156 _diagnostics
.warning("Could not find __TEXT/__fips_hmacs section in libcorecrypto.dylib, skipping FIPS sealing");
3159 if ( hashStoreSize
!= 32 ) {
3160 _diagnostics
.warning("__TEXT/__fips_hmacs section in libcorecrypto.dylib is not 32 bytes in size, skipping FIPS sealing");
3164 // compute hmac hash of __text section
3166 const void* textLocation
= ml
->findSectionContent("__TEXT", "__text", textSize
);
3167 if ( textLocation
== nullptr ) {
3168 _diagnostics
.warning("Could not find __TEXT/__text section in libcorecrypto.dylib, skipping FIPS sealing");
3171 unsigned char hmac_key
= 0;
3172 CCHmac(kCCHmacAlgSHA256
, &hmac_key
, 1, textLocation
, textSize
, (void*)hashStoreLocation
); // store hash directly into hashStoreLocation
3175 void SharedCacheBuilder::codeSign()
3177 uint8_t dscHashType
;
3178 uint8_t dscHashSize
;
3179 uint32_t dscDigestFormat
;
3182 // select which codesigning hash
3183 switch (_options
.codeSigningDigestMode
) {
3184 case DyldSharedCache::Agile
:
3186 // Fall through to SHA1, because the main code directory remains SHA1 for compatibility.
3187 [[clang::fallthrough]];
3188 case DyldSharedCache::SHA1only
:
3189 #pragma clang diagnostic push
3190 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
3191 dscHashType
= CS_HASHTYPE_SHA1
;
3192 dscHashSize
= CS_HASH_SIZE_SHA1
;
3193 dscDigestFormat
= kCCDigestSHA1
;
3194 #pragma clang diagnostic pop
3196 case DyldSharedCache::SHA256only
:
3197 dscHashType
= CS_HASHTYPE_SHA256
;
3198 dscHashSize
= CS_HASH_SIZE_SHA256
;
3199 dscDigestFormat
= kCCDigestSHA256
;
3202 _diagnostics
.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.",
3203 _options
.codeSigningDigestMode
);
3207 std::string cacheIdentifier
= "com.apple.dyld.cache.";
3208 cacheIdentifier
+= _options
.archs
->name();
3209 if ( _options
.dylibsRemovedDuringMastering
) {
3210 if ( _options
.optimizeStubs
)
3211 cacheIdentifier
+= ".release";
3213 cacheIdentifier
+= ".development";
3215 // get pointers into shared cache buffer
3216 size_t inBbufferSize
= _readExecuteRegion
.sizeInUse
+dataRegionsSizeInUse()+_readOnlyRegion
.sizeInUse
+_localSymbolsRegion
.sizeInUse
;
3218 const uint16_t pageSize
= _archLayout
->csPageSize
;
3220 // layout code signature contents
3221 uint32_t blobCount
= agile
? 4 : 3;
3222 size_t idSize
= cacheIdentifier
.size()+1; // +1 for terminating 0
3223 uint32_t slotCount
= (uint32_t)((inBbufferSize
+ pageSize
- 1) / pageSize
);
3224 uint32_t xSlotCount
= CSSLOT_REQUIREMENTS
;
3225 size_t idOffset
= offsetof(CS_CodeDirectory
, end_withExecSeg
);
3226 size_t hashOffset
= idOffset
+idSize
+ dscHashSize
*xSlotCount
;
3227 size_t hash256Offset
= idOffset
+idSize
+ CS_HASH_SIZE_SHA256
*xSlotCount
;
3228 size_t cdSize
= hashOffset
+ (slotCount
* dscHashSize
);
3229 size_t cd256Size
= agile
? hash256Offset
+ (slotCount
* CS_HASH_SIZE_SHA256
) : 0;
3230 size_t reqsSize
= 12;
3231 size_t cmsSize
= sizeof(CS_Blob
);
3232 size_t cdOffset
= sizeof(CS_SuperBlob
) + blobCount
*sizeof(CS_BlobIndex
);
3233 size_t cd256Offset
= cdOffset
+ cdSize
;
3234 size_t reqsOffset
= cd256Offset
+ cd256Size
; // equals cdOffset + cdSize if not agile
3235 size_t cmsOffset
= reqsOffset
+ reqsSize
;
3236 size_t sbSize
= cmsOffset
+ cmsSize
;
3237 size_t sigSize
= align(sbSize
, 14); // keep whole cache 16KB aligned
3239 // allocate space for blob
3240 vm_address_t codeSigAlloc
;
3241 if ( vm_allocate(mach_task_self(), &codeSigAlloc
, sigSize
, VM_FLAGS_ANYWHERE
) != 0 ) {
3242 _diagnostics
.error("could not allocate code signature buffer");
3245 _codeSignatureRegion
.buffer
= (uint8_t*)codeSigAlloc
;
3246 _codeSignatureRegion
.bufferSize
= sigSize
;
3247 _codeSignatureRegion
.sizeInUse
= sigSize
;
3249 // create overall code signature which is a superblob
3250 CS_SuperBlob
* sb
= reinterpret_cast<CS_SuperBlob
*>(_codeSignatureRegion
.buffer
);
3251 sb
->magic
= htonl(CSMAGIC_EMBEDDED_SIGNATURE
);
3252 sb
->length
= htonl(sbSize
);
3253 sb
->count
= htonl(blobCount
);
3254 sb
->index
[0].type
= htonl(CSSLOT_CODEDIRECTORY
);
3255 sb
->index
[0].offset
= htonl(cdOffset
);
3256 sb
->index
[1].type
= htonl(CSSLOT_REQUIREMENTS
);
3257 sb
->index
[1].offset
= htonl(reqsOffset
);
3258 sb
->index
[2].type
= htonl(CSSLOT_CMS_SIGNATURE
);
3259 sb
->index
[2].offset
= htonl(cmsOffset
);
3261 sb
->index
[3].type
= htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES
+ 0);
3262 sb
->index
[3].offset
= htonl(cd256Offset
);
3265 // fill in empty requirements
3266 CS_RequirementsBlob
* reqs
= (CS_RequirementsBlob
*)(((char*)sb
)+reqsOffset
);
3267 reqs
->magic
= htonl(CSMAGIC_REQUIREMENTS
);
3268 reqs
->length
= htonl(sizeof(CS_RequirementsBlob
));
3271 // initialize fixed fields of Code Directory
3272 CS_CodeDirectory
* cd
= (CS_CodeDirectory
*)(((char*)sb
)+cdOffset
);
3273 cd
->magic
= htonl(CSMAGIC_CODEDIRECTORY
);
3274 cd
->length
= htonl(cdSize
);
3275 cd
->version
= htonl(0x20400); // supports exec segment
3276 cd
->flags
= htonl(kSecCodeSignatureAdhoc
);
3277 cd
->hashOffset
= htonl(hashOffset
);
3278 cd
->identOffset
= htonl(idOffset
);
3279 cd
->nSpecialSlots
= htonl(xSlotCount
);
3280 cd
->nCodeSlots
= htonl(slotCount
);
3281 cd
->codeLimit
= htonl(inBbufferSize
);
3282 cd
->hashSize
= dscHashSize
;
3283 cd
->hashType
= dscHashType
;
3284 cd
->platform
= 0; // not platform binary
3285 cd
->pageSize
= __builtin_ctz(pageSize
); // log2(CS_PAGE_SIZE);
3286 cd
->spare2
= 0; // unused (must be zero)
3287 cd
->scatterOffset
= 0; // not supported anymore
3288 cd
->teamOffset
= 0; // no team ID
3289 cd
->spare3
= 0; // unused (must be zero)
3290 cd
->codeLimit64
= 0; // falls back to codeLimit
3292 // executable segment info
3293 cd
->execSegBase
= htonll(_readExecuteRegion
.cacheFileOffset
); // base of TEXT segment
3294 cd
->execSegLimit
= htonll(_readExecuteRegion
.sizeInUse
); // size of TEXT segment
3295 cd
->execSegFlags
= 0; // not a main binary
3297 // initialize dynamic fields of Code Directory
3298 strcpy((char*)cd
+ idOffset
, cacheIdentifier
.c_str());
3300 // add special slot hashes
3301 uint8_t* hashSlot
= (uint8_t*)cd
+ hashOffset
;
3302 uint8_t* reqsHashSlot
= &hashSlot
[-CSSLOT_REQUIREMENTS
*dscHashSize
];
3303 CCDigest(dscDigestFormat
, (uint8_t*)reqs
, sizeof(CS_RequirementsBlob
), reqsHashSlot
);
3305 CS_CodeDirectory
* cd256
;
3306 uint8_t* hash256Slot
;
3307 uint8_t* reqsHash256Slot
;
3309 // Note that the assumption here is that the size up to the hashes is the same as for
3310 // sha1 code directory, and that they come last, after everything else.
3312 cd256
= (CS_CodeDirectory
*)(((char*)sb
)+cd256Offset
);
3313 cd256
->magic
= htonl(CSMAGIC_CODEDIRECTORY
);
3314 cd256
->length
= htonl(cd256Size
);
3315 cd256
->version
= htonl(0x20400); // supports exec segment
3316 cd256
->flags
= htonl(kSecCodeSignatureAdhoc
);
3317 cd256
->hashOffset
= htonl(hash256Offset
);
3318 cd256
->identOffset
= htonl(idOffset
);
3319 cd256
->nSpecialSlots
= htonl(xSlotCount
);
3320 cd256
->nCodeSlots
= htonl(slotCount
);
3321 cd256
->codeLimit
= htonl(inBbufferSize
);
3322 cd256
->hashSize
= CS_HASH_SIZE_SHA256
;
3323 cd256
->hashType
= CS_HASHTYPE_SHA256
;
3324 cd256
->platform
= 0; // not platform binary
3325 cd256
->pageSize
= __builtin_ctz(pageSize
); // log2(CS_PAGE_SIZE);
3326 cd256
->spare2
= 0; // unused (must be zero)
3327 cd256
->scatterOffset
= 0; // not supported anymore
3328 cd256
->teamOffset
= 0; // no team ID
3329 cd256
->spare3
= 0; // unused (must be zero)
3330 cd256
->codeLimit64
= 0; // falls back to codeLimit
3332 // executable segment info
3333 cd256
->execSegBase
= cd
->execSegBase
;
3334 cd256
->execSegLimit
= cd
->execSegLimit
;
3335 cd256
->execSegFlags
= cd
->execSegFlags
;
3337 // initialize dynamic fields of Code Directory
3338 strcpy((char*)cd256
+ idOffset
, cacheIdentifier
.c_str());
3340 // add special slot hashes
3341 hash256Slot
= (uint8_t*)cd256
+ hash256Offset
;
3342 reqsHash256Slot
= &hash256Slot
[-CSSLOT_REQUIREMENTS
*CS_HASH_SIZE_SHA256
];
3343 CCDigest(kCCDigestSHA256
, (uint8_t*)reqs
, sizeof(CS_RequirementsBlob
), reqsHash256Slot
);
3348 reqsHash256Slot
= NULL
;
3351 // fill in empty CMS blob for ad-hoc signing
3352 CS_Blob
* cms
= (CS_Blob
*)(((char*)sb
)+cmsOffset
);
3353 cms
->magic
= htonl(CSMAGIC_BLOBWRAPPER
);
3354 cms
->length
= htonl(sizeof(CS_Blob
));
3357 // alter header of cache to record size and location of code signature
3358 // do this *before* hashing each page
3359 dyld_cache_header
* cache
= (dyld_cache_header
*)_readExecuteRegion
.buffer
;
3360 cache
->codeSignatureOffset
= inBbufferSize
;
3361 cache
->codeSignatureSize
= sigSize
;
3366 const uint8_t* buffer
= nullptr;
3368 std::vector
<SlotRange
> regionSlots
;
3370 regionSlots
.push_back({ 0, (_readExecuteRegion
.sizeInUse
/ pageSize
), _readExecuteRegion
.buffer
});
3372 for (const Region
& dataRegion
: _dataRegions
) {
3373 // The first data region starts at the end of __TEXT, and subsequent regions are
3374 // after the previous __DATA region.
3375 uint64_t previousEnd
= regionSlots
.back().end
;
3376 uint64_t numSlots
= dataRegion
.sizeInUse
/ pageSize
;
3377 regionSlots
.push_back({ previousEnd
, previousEnd
+ numSlots
, dataRegion
.buffer
});
3381 uint64_t previousEnd
= regionSlots
.back().end
;
3382 uint64_t numSlots
= _readOnlyRegion
.sizeInUse
/ pageSize
;
3383 regionSlots
.push_back({ previousEnd
, previousEnd
+ numSlots
, _readOnlyRegion
.buffer
});
3386 if ( _localSymbolsRegion
.sizeInUse
!= 0 ) {
3387 uint64_t previousEnd
= regionSlots
.back().end
;
3388 uint64_t numSlots
= _localSymbolsRegion
.sizeInUse
/ pageSize
;
3389 regionSlots
.push_back({ previousEnd
, previousEnd
+ numSlots
, _localSymbolsRegion
.buffer
});
3392 auto codeSignPage
= ^(size_t i
) {
3393 // move to correct region
3394 for (const SlotRange
& slotRange
: regionSlots
) {
3395 if ( (i
>= slotRange
.start
) && (i
< slotRange
.end
) ) {
3396 const uint8_t* code
= slotRange
.buffer
+ ((i
- slotRange
.start
) * pageSize
);
3398 CCDigest(dscDigestFormat
, code
, pageSize
, hashSlot
+ (i
* dscHashSize
));
3401 CCDigest(kCCDigestSHA256
, code
, pageSize
, hash256Slot
+ (i
* CS_HASH_SIZE_SHA256
));
3406 assert(0 && "Out of range slot");
3410 dispatch_apply(slotCount
, DISPATCH_APPLY_AUTO
, ^(size_t i
) {
3414 // Now that we have a code signature, compute a cache UUID by hashing the code signature blob
3416 uint8_t* uuidLoc
= cache
->uuid
;
3417 assert(uuid_is_null(uuidLoc
));
3418 static_assert(offsetof(dyld_cache_header
, uuid
) / CS_PAGE_SIZE_4K
== 0, "uuid is expected in the first page of the cache");
3419 uint8_t fullDigest
[CC_SHA256_DIGEST_LENGTH
];
3420 CC_SHA256((const void*)cd
, (unsigned)cdSize
, fullDigest
);
3421 memcpy(uuidLoc
, fullDigest
, 16);
3422 // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
3423 uuidLoc
[6] = ( uuidLoc
[6] & 0x0F ) | ( 3 << 4 );
3424 uuidLoc
[8] = ( uuidLoc
[8] & 0x3F ) | 0x80;
3426 // Now codesign page 0 again, because we modified it by setting uuid in header
3430 // hash of entire code directory (cdHash) uses same hash as each page
3431 uint8_t fullCdHash
[dscHashSize
];
3432 CCDigest(dscDigestFormat
, (const uint8_t*)cd
, cdSize
, fullCdHash
);
3433 // Note: cdHash is defined as first 20 bytes of hash
3434 memcpy(_cdHashFirst
, fullCdHash
, 20);
3436 uint8_t fullCdHash256
[CS_HASH_SIZE_SHA256
];
3437 CCDigest(kCCDigestSHA256
, (const uint8_t*)cd256
, cd256Size
, fullCdHash256
);
3438 // Note: cdHash is defined as first 20 bytes of hash, even for sha256
3439 memcpy(_cdHashSecond
, fullCdHash256
, 20);
3442 memset(_cdHashSecond
, 0, 20);
3446 const bool SharedCacheBuilder::agileSignature()
3448 return _options
.codeSigningDigestMode
== DyldSharedCache::Agile
;
3451 static const std::string
cdHash(uint8_t hash
[20])
3454 for (int i
= 0; i
< 20; ++i
)
3455 sprintf(&buff
[2*i
], "%2.2x", hash
[i
]);
3459 const std::string
SharedCacheBuilder::cdHashFirst()
3461 return cdHash(_cdHashFirst
);
3464 const std::string
SharedCacheBuilder::cdHashSecond()
3466 return cdHash(_cdHashSecond
);
3469 const std::string
SharedCacheBuilder::uuid() const
3471 dyld_cache_header
* cache
= (dyld_cache_header
*)_readExecuteRegion
.buffer
;
3472 uuid_string_t uuidStr
;
3473 uuid_unparse(cache
->uuid
, uuidStr
);
3477 void SharedCacheBuilder::forEachDylibInfo(void (^callback
)(const DylibInfo
& dylib
, Diagnostics
& dylibDiag
)) {
3478 for (const DylibInfo
& dylibInfo
: _sortedDylibs
) {
3479 // The shared cache builder doesn't use per-dylib errors right now
3480 // so just share the global diagnostics
3481 callback(dylibInfo
, _diagnostics
);
3487 template <typename P
>
3488 bool SharedCacheBuilder::makeRebaseChainV2(uint8_t* pageContent
, uint16_t lastLocationOffset
, uint16_t offset
, const dyld_cache_slide_info2
* info
)
3490 typedef typename
P::uint_t pint_t
;
3492 const pint_t deltaMask
= (pint_t
)(info
->delta_mask
);
3493 const pint_t valueMask
= ~deltaMask
;
3494 const pint_t valueAdd
= (pint_t
)(info
->value_add
);
3495 const unsigned deltaShift
= __builtin_ctzll(deltaMask
) - 2;
3496 const uint32_t maxDelta
= (uint32_t)(deltaMask
>> deltaShift
);
3498 pint_t
* lastLoc
= (pint_t
*)&pageContent
[lastLocationOffset
+0];
3499 pint_t lastValue
= (pint_t
)P::getP(*lastLoc
);
3500 if ( (lastValue
- valueAdd
) & deltaMask
) {
3501 std::string dylibName
;
3502 std::string segName
;
3503 findDylibAndSegment((void*)pageContent
, dylibName
, segName
);
3504 _diagnostics
.error("rebase pointer (0x%0lX) does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
3505 (long)lastValue
, lastLocationOffset
, segName
.c_str(), dylibName
.c_str());
3508 if ( offset
<= (lastLocationOffset
+maxDelta
) ) {
3509 // previous location in range, make link from it
3510 // encode this location into last value
3511 pint_t delta
= offset
- lastLocationOffset
;
3512 pint_t newLastValue
= ((lastValue
- valueAdd
) & valueMask
) | (delta
<< deltaShift
);
3513 //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
3514 // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
3516 if ( _aslrTracker
.hasHigh8(lastLoc
, &highByte
) ) {
3517 uint64_t tbi
= (uint64_t)highByte
<< 56;
3518 newLastValue
|= tbi
;
3520 P::setP(*lastLoc
, newLastValue
);
3523 //fprintf(stderr, " too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
3525 // distance between rebase locations is too far
3526 // see if we can make a chain from non-rebase locations
3527 uint16_t nonRebaseLocationOffsets
[1024];
3528 unsigned nrIndex
= 0;
3529 for (uint16_t i
= lastLocationOffset
; i
< offset
-maxDelta
; ) {
3530 nonRebaseLocationOffsets
[nrIndex
] = 0;
3531 for (int j
=maxDelta
; j
> 0; j
-= 4) {
3532 pint_t value
= (pint_t
)P::getP(*(pint_t
*)&pageContent
[i
+j
]);
3534 // Steal values of 0 to be used in the rebase chain
3535 nonRebaseLocationOffsets
[nrIndex
] = i
+j
;
3539 if ( nonRebaseLocationOffsets
[nrIndex
] == 0 ) {
3540 lastValue
= (pint_t
)P::getP(*lastLoc
);
3541 pint_t newValue
= ((lastValue
- valueAdd
) & valueMask
);
3542 //warning(" no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX", lastLocationOffset, (long)value, (long)newValue);
3543 P::setP(*lastLoc
, newValue
);
3546 i
= nonRebaseLocationOffsets
[nrIndex
];
3550 // we can make chain. go back and add each non-rebase location to chain
3551 uint16_t prevOffset
= lastLocationOffset
;
3552 pint_t
* prevLoc
= (pint_t
*)&pageContent
[prevOffset
];
3553 for (unsigned n
=0; n
< nrIndex
; ++n
) {
3554 uint16_t nOffset
= nonRebaseLocationOffsets
[n
];
3555 assert(nOffset
!= 0);
3556 pint_t
* nLoc
= (pint_t
*)&pageContent
[nOffset
];
3557 pint_t delta2
= nOffset
- prevOffset
;
3558 pint_t value
= (pint_t
)P::getP(*prevLoc
);
3561 newValue
= (delta2
<< deltaShift
);
3563 newValue
= ((value
- valueAdd
) & valueMask
) | (delta2
<< deltaShift
);
3564 //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
3565 P::setP(*prevLoc
, newValue
);
3566 prevOffset
= nOffset
;
3569 pint_t delta3
= offset
- prevOffset
;
3570 pint_t value
= (pint_t
)P::getP(*prevLoc
);
3573 newValue
= (delta3
<< deltaShift
);
3575 newValue
= ((value
- valueAdd
) & valueMask
) | (delta3
<< deltaShift
);
3576 //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
3577 P::setP(*prevLoc
, newValue
);
3583 template <typename P
>
3584 void SharedCacheBuilder::addPageStartsV2(uint8_t* pageContent
, const bool bitmap
[], const dyld_cache_slide_info2
* info
,
3585 std::vector
<uint16_t>& pageStarts
, std::vector
<uint16_t>& pageExtras
)
3587 typedef typename
P::uint_t pint_t
;
3589 const pint_t deltaMask
= (pint_t
)(info
->delta_mask
);
3590 const pint_t valueMask
= ~deltaMask
;
3591 const uint32_t pageSize
= info
->page_size
;
3592 const pint_t valueAdd
= (pint_t
)(info
->value_add
);
3594 uint16_t startValue
= DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE
;
3595 uint16_t lastLocationOffset
= 0xFFFF;
3596 for(uint32_t i
=0; i
< pageSize
/4; ++i
) {
3597 unsigned offset
= i
*4;
3599 if ( startValue
== DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE
) {
3600 // found first rebase location in page
3603 else if ( !makeRebaseChainV2
<P
>(pageContent
, lastLocationOffset
, offset
, info
) ) {
3604 // can't record all rebasings in one chain
3605 if ( (startValue
& DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
) == 0 ) {
3606 // switch page_start to "extras" which is a list of chain starts
3607 unsigned indexInExtras
= (unsigned)pageExtras
.size();
3608 if ( indexInExtras
> 0x3FFF ) {
3609 _diagnostics
.error("rebase overflow in v2 page extras");
3612 pageExtras
.push_back(startValue
);
3613 startValue
= indexInExtras
| DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
;
3615 pageExtras
.push_back(i
);
3617 lastLocationOffset
= offset
;
3620 if ( lastLocationOffset
!= 0xFFFF ) {
3621 // mark end of chain
3622 pint_t
* lastLoc
= (pint_t
*)&pageContent
[lastLocationOffset
];
3623 pint_t lastValue
= (pint_t
)P::getP(*lastLoc
);
3624 pint_t newValue
= ((lastValue
- valueAdd
) & valueMask
);
3625 P::setP(*lastLoc
, newValue
);
3627 if ( startValue
& DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
) {
3628 // add end bit to extras
3629 pageExtras
.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END
;
3631 pageStarts
.push_back(startValue
);
3634 template <typename P
>
3635 void SharedCacheBuilder::writeSlideInfoV2(const bool bitmapForAllDataRegions
[], unsigned dataPageCountForAllDataRegions
)
3637 typedef typename
P::uint_t pint_t
;
3638 typedef typename
P::E E
;
3640 const uint32_t pageSize
= _aslrTracker
.pageSize();
3641 const uint8_t* firstDataRegionBuffer
= firstDataRegion()->buffer
;
3642 for (uint32_t dataRegionIndex
= 0; dataRegionIndex
!= _dataRegions
.size(); ++dataRegionIndex
) {
3643 Region
& dataRegion
= _dataRegions
[dataRegionIndex
];
3645 // fill in fixed info
3646 assert(dataRegion
.slideInfoFileOffset
!= 0);
3647 assert((dataRegion
.sizeInUse
% pageSize
) == 0);
3648 unsigned dataPageCount
= (uint32_t)dataRegion
.sizeInUse
/ pageSize
;
3649 dyld_cache_slide_info2
* info
= (dyld_cache_slide_info2
*)dataRegion
.slideInfoBuffer
;
3651 info
->page_size
= pageSize
;
3652 info
->delta_mask
= _archLayout
->pointerDeltaMask
;
3653 info
->value_add
= _archLayout
->useValueAdd
? _archLayout
->sharedMemoryStart
: 0;
3655 // set page starts and extras for each page
3656 std::vector
<uint16_t> pageStarts
;
3657 std::vector
<uint16_t> pageExtras
;
3658 pageStarts
.reserve(dataPageCount
);
3660 const size_t bitmapEntriesPerPage
= (sizeof(bool)*(pageSize
/4));
3661 uint8_t* pageContent
= dataRegion
.buffer
;
3662 unsigned numPagesFromFirstDataRegion
= (uint32_t)(dataRegion
.buffer
- firstDataRegionBuffer
) / pageSize
;
3663 assert((numPagesFromFirstDataRegion
+ dataPageCount
) <= dataPageCountForAllDataRegions
);
3664 const bool* bitmapForRegion
= (const bool*)bitmapForAllDataRegions
+ (bitmapEntriesPerPage
* numPagesFromFirstDataRegion
);
3665 const bool* bitmapForPage
= bitmapForRegion
;
3666 for (unsigned i
=0; i
< dataPageCount
; ++i
) {
3667 //warning("page[%d]", i);
3668 addPageStartsV2
<P
>(pageContent
, bitmapForPage
, info
, pageStarts
, pageExtras
);
3669 if ( _diagnostics
.hasError() ) {
3672 pageContent
+= pageSize
;
3673 bitmapForPage
+= (sizeof(bool)*(pageSize
/4));
3676 // fill in computed info
3677 info
->page_starts_offset
= sizeof(dyld_cache_slide_info2
);
3678 info
->page_starts_count
= (unsigned)pageStarts
.size();
3679 info
->page_extras_offset
= (unsigned)(sizeof(dyld_cache_slide_info2
)+pageStarts
.size()*sizeof(uint16_t));
3680 info
->page_extras_count
= (unsigned)pageExtras
.size();
3681 uint16_t* pageStartsBuffer
= (uint16_t*)((char*)info
+ info
->page_starts_offset
);
3682 uint16_t* pageExtrasBuffer
= (uint16_t*)((char*)info
+ info
->page_extras_offset
);
3683 for (unsigned i
=0; i
< pageStarts
.size(); ++i
)
3684 pageStartsBuffer
[i
] = pageStarts
[i
];
3685 for (unsigned i
=0; i
< pageExtras
.size(); ++i
)
3686 pageExtrasBuffer
[i
] = pageExtras
[i
];
3687 // update header with final size
3688 uint64_t slideInfoSize
= align(info
->page_extras_offset
+ pageExtras
.size()*sizeof(uint16_t), _archLayout
->sharedRegionAlignP2
);
3689 dataRegion
.slideInfoFileSize
= slideInfoSize
;
3690 if ( dataRegion
.slideInfoFileSize
> dataRegion
.slideInfoBufferSizeAllocated
) {
3691 _diagnostics
.error("kernel slide info overflow buffer");
3693 // Update the mapping entry on the cache header
3694 const dyld_cache_header
* cacheHeader
= (dyld_cache_header
*)_readExecuteRegion
.buffer
;
3695 dyld_cache_mapping_and_slide_info
* slidableMappings
= (dyld_cache_mapping_and_slide_info
*)(_readExecuteRegion
.buffer
+ cacheHeader
->mappingWithSlideOffset
);
3696 slidableMappings
[1 + dataRegionIndex
].slideInfoFileSize
= dataRegion
.slideInfoFileSize
;
3697 //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
3701 #if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
3702 // fits in to int16_t
3703 static bool smallValue(uint64_t value
)
3705 uint32_t high
= (value
& 0xFFFF8000);
3706 return (high
== 0) || (high
== 0xFFFF8000);
3709 template <typename P
>
3710 bool SharedCacheBuilder::makeRebaseChainV4(uint8_t* pageContent
, uint16_t lastLocationOffset
, uint16_t offset
, const dyld_cache_slide_info4
* info
)
3712 typedef typename
P::uint_t pint_t
;
3714 const pint_t deltaMask
= (pint_t
)(info
->delta_mask
);
3715 const pint_t valueMask
= ~deltaMask
;
3716 const pint_t valueAdd
= (pint_t
)(info
->value_add
);
3717 const unsigned deltaShift
= __builtin_ctzll(deltaMask
) - 2;
3718 const uint32_t maxDelta
= (uint32_t)(deltaMask
>> deltaShift
);
3720 pint_t
* lastLoc
= (pint_t
*)&pageContent
[lastLocationOffset
+0];
3721 pint_t lastValue
= (pint_t
)P::getP(*lastLoc
);
3722 if ( (lastValue
- valueAdd
) & deltaMask
) {
3723 std::string dylibName
;
3724 std::string segName
;
3725 findDylibAndSegment((void*)pageContent
, dylibName
, segName
);
3726 _diagnostics
.error("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n",
3727 lastLocationOffset
, segName
.c_str(), dylibName
.c_str());
3730 if ( offset
<= (lastLocationOffset
+maxDelta
) ) {
3731 // previous location in range, make link from it
3732 // encode this location into last value
3733 pint_t delta
= offset
- lastLocationOffset
;
3734 pint_t newLastValue
= ((lastValue
- valueAdd
) & valueMask
) | (delta
<< deltaShift
);
3735 //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX",
3736 // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue);
3737 P::setP(*lastLoc
, newLastValue
);
3740 //fprintf(stderr, " too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
3742 // distance between rebase locations is too far
3743 // see if we can make a chain from non-rebase locations
3744 uint16_t nonRebaseLocationOffsets
[1024];
3745 unsigned nrIndex
= 0;
3746 for (uint16_t i
= lastLocationOffset
; i
< offset
-maxDelta
; ) {
3747 nonRebaseLocationOffsets
[nrIndex
] = 0;
3748 for (int j
=maxDelta
; j
> 0; j
-= 4) {
3749 pint_t value
= (pint_t
)P::getP(*(pint_t
*)&pageContent
[i
+j
]);
3750 if ( smallValue(value
) ) {
3751 // Steal values of 0 to be used in the rebase chain
3752 nonRebaseLocationOffsets
[nrIndex
] = i
+j
;
3756 if ( nonRebaseLocationOffsets
[nrIndex
] == 0 ) {
3757 lastValue
= (pint_t
)P::getP(*lastLoc
);
3758 pint_t newValue
= ((lastValue
- valueAdd
) & valueMask
);
3759 //fprintf(stderr, " no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX\n",
3760 // lastLocationOffset, (long)lastValue, (long)newValue);
3761 P::setP(*lastLoc
, newValue
);
3764 i
= nonRebaseLocationOffsets
[nrIndex
];
3768 // we can make chain. go back and add each non-rebase location to chain
3769 uint16_t prevOffset
= lastLocationOffset
;
3770 pint_t
* prevLoc
= (pint_t
*)&pageContent
[prevOffset
];
3771 for (unsigned n
=0; n
< nrIndex
; ++n
) {
3772 uint16_t nOffset
= nonRebaseLocationOffsets
[n
];
3773 assert(nOffset
!= 0);
3774 pint_t
* nLoc
= (pint_t
*)&pageContent
[nOffset
];
3775 uint32_t delta2
= nOffset
- prevOffset
;
3776 pint_t value
= (pint_t
)P::getP(*prevLoc
);
3778 if ( smallValue(value
) )
3779 newValue
= (value
& valueMask
) | (delta2
<< deltaShift
);
3781 newValue
= ((value
- valueAdd
) & valueMask
) | (delta2
<< deltaShift
);
3782 //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue);
3783 P::setP(*prevLoc
, newValue
);
3784 prevOffset
= nOffset
;
3787 uint32_t delta3
= offset
- prevOffset
;
3788 pint_t value
= (pint_t
)P::getP(*prevLoc
);
3790 if ( smallValue(value
) )
3791 newValue
= (value
& valueMask
) | (delta3
<< deltaShift
);
3793 newValue
= ((value
- valueAdd
) & valueMask
) | (delta3
<< deltaShift
);
3794 //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue);
3795 P::setP(*prevLoc
, newValue
);
3801 template <typename P
>
3802 void SharedCacheBuilder::addPageStartsV4(uint8_t* pageContent
, const bool bitmap
[], const dyld_cache_slide_info4
* info
,
3803 std::vector
<uint16_t>& pageStarts
, std::vector
<uint16_t>& pageExtras
)
3805 typedef typename
P::uint_t pint_t
;
3807 const pint_t deltaMask
= (pint_t
)(info
->delta_mask
);
3808 const pint_t valueMask
= ~deltaMask
;
3809 const uint32_t pageSize
= info
->page_size
;
3810 const pint_t valueAdd
= (pint_t
)(info
->value_add
);
3812 uint16_t startValue
= DYLD_CACHE_SLIDE4_PAGE_NO_REBASE
;
3813 uint16_t lastLocationOffset
= 0xFFFF;
3814 for(uint32_t i
=0; i
< pageSize
/4; ++i
) {
3815 unsigned offset
= i
*4;
3817 if ( startValue
== DYLD_CACHE_SLIDE4_PAGE_NO_REBASE
) {
3818 // found first rebase location in page
3821 else if ( !makeRebaseChainV4
<P
>(pageContent
, lastLocationOffset
, offset
, info
) ) {
3822 // can't record all rebasings in one chain
3823 if ( (startValue
& DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA
) == 0 ) {
3824 // switch page_start to "extras" which is a list of chain starts
3825 unsigned indexInExtras
= (unsigned)pageExtras
.size();
3826 if ( indexInExtras
>= DYLD_CACHE_SLIDE4_PAGE_INDEX
) {
3827 _diagnostics
.error("rebase overflow in v4 page extras");
3830 pageExtras
.push_back(startValue
);
3831 startValue
= indexInExtras
| DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA
;
3833 pageExtras
.push_back(i
);
3835 lastLocationOffset
= offset
;
3838 if ( lastLocationOffset
!= 0xFFFF ) {
3839 // mark end of chain
3840 pint_t
* lastLoc
= (pint_t
*)&pageContent
[lastLocationOffset
];
3841 pint_t lastValue
= (pint_t
)P::getP(*lastLoc
);
3842 pint_t newValue
= ((lastValue
- valueAdd
) & valueMask
);
3843 P::setP(*lastLoc
, newValue
);
3844 if ( startValue
& DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA
) {
3845 // add end bit to extras
3846 pageExtras
.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END
;
3849 pageStarts
.push_back(startValue
);
3854 template <typename P
>
3855 void SharedCacheBuilder::writeSlideInfoV4(const bool bitmapForAllDataRegions
[], unsigned dataPageCountForAllDataRegions
)
3857 typedef typename
P::uint_t pint_t
;
3858 typedef typename
P::E E
;
3860 const uint32_t pageSize
= _aslrTracker
.pageSize();
3861 const uint8_t* firstDataRegionBuffer
= firstDataRegion()->buffer
;
3862 for (uint32_t dataRegionIndex
= 0; dataRegionIndex
!= _dataRegions
.size(); ++dataRegionIndex
) {
3863 Region
& dataRegion
= _dataRegions
[dataRegionIndex
];
3865 // fill in fixed info
3866 assert(dataRegion
.slideInfoFileOffset
!= 0);
3867 assert((dataRegion
.sizeInUse
% pageSize
) == 0);
3868 unsigned dataPageCount
= (uint32_t)dataRegion
.sizeInUse
/ pageSize
;
3869 dyld_cache_slide_info4
* info
= (dyld_cache_slide_info4
*)dataRegion
.slideInfoBuffer
;
3871 info
->page_size
= pageSize
;
3872 info
->delta_mask
= _archLayout
->pointerDeltaMask
;
3873 info
->value_add
= info
->value_add
= _archLayout
->useValueAdd
? _archLayout
->sharedMemoryStart
: 0;
3875 // set page starts and extras for each page
3876 std::vector
<uint16_t> pageStarts
;
3877 std::vector
<uint16_t> pageExtras
;
3878 pageStarts
.reserve(dataPageCount
);
3879 const size_t bitmapEntriesPerPage
= (sizeof(bool)*(pageSize
/4));
3880 uint8_t* pageContent
= dataRegion
.buffer
;
3881 unsigned numPagesFromFirstDataRegion
= (uint32_t)(dataRegion
.buffer
- firstDataRegionBuffer
) / pageSize
;
3882 assert((numPagesFromFirstDataRegion
+ dataPageCount
) <= dataPageCountForAllDataRegions
);
3883 const bool* bitmapForRegion
= (const bool*)bitmapForAllDataRegions
+ (bitmapEntriesPerPage
* numPagesFromFirstDataRegion
);
3884 const bool* bitmapForPage
= bitmapForRegion
;
3885 for (unsigned i
=0; i
< dataPageCount
; ++i
) {
3886 addPageStartsV4
<P
>(pageContent
, bitmapForPage
, info
, pageStarts
, pageExtras
);
3887 if ( _diagnostics
.hasError() ) {
3890 pageContent
+= pageSize
;
3891 bitmapForPage
+= (sizeof(bool)*(pageSize
/4));
3893 // fill in computed info
3894 info
->page_starts_offset
= sizeof(dyld_cache_slide_info4
);
3895 info
->page_starts_count
= (unsigned)pageStarts
.size();
3896 info
->page_extras_offset
= (unsigned)(sizeof(dyld_cache_slide_info4
)+pageStarts
.size()*sizeof(uint16_t));
3897 info
->page_extras_count
= (unsigned)pageExtras
.size();
3898 uint16_t* pageStartsBuffer
= (uint16_t*)((char*)info
+ info
->page_starts_offset
);
3899 uint16_t* pageExtrasBuffer
= (uint16_t*)((char*)info
+ info
->page_extras_offset
);
3900 for (unsigned i
=0; i
< pageStarts
.size(); ++i
)
3901 pageStartsBuffer
[i
] = pageStarts
[i
];
3902 for (unsigned i
=0; i
< pageExtras
.size(); ++i
)
3903 pageExtrasBuffer
[i
] = pageExtras
[i
];
3904 // update header with final size
3905 uint64_t slideInfoSize
= align(info
->page_extras_offset
+ pageExtras
.size()*sizeof(uint16_t), _archLayout
->sharedRegionAlignP2
);
3906 dataRegion
.slideInfoFileSize
= slideInfoSize
;
3907 if ( dataRegion
.slideInfoFileSize
> dataRegion
.slideInfoBufferSizeAllocated
) {
3908 _diagnostics
.error("kernel slide info overflow buffer");
3910 // Update the mapping entry on the cache header
3911 const dyld_cache_header
* cacheHeader
= (dyld_cache_header
*)_readExecuteRegion
.buffer
;
3912 dyld_cache_mapping_and_slide_info
* slidableMappings
= (dyld_cache_mapping_and_slide_info
*)(_readExecuteRegion
.buffer
+ cacheHeader
->mappingWithSlideOffset
);
3913 slidableMappings
[1 + dataRegionIndex
].slideInfoFileSize
= dataRegion
.slideInfoFileSize
;
3914 //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
3920 void CacheBuilder::writeSlideInfoV1()
3922 // build one 128-byte bitmap per page (4096) of DATA
3923 uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset;
3924 uint8_t* const dataEnd = dataStart + regions[1].size;
3925 const long bitmapSize = (dataEnd - dataStart)/(4*8);
3926 uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1);
3927 for (void* p : _pointersForASLR) {
3928 if ( (p < dataStart) || ( p > dataEnd) )
3929 terminate("DATA pointer for sliding, out of range\n");
3930 long offset = (long)((uint8_t*)p - dataStart);
3931 if ( (offset % 4) != 0 )
3932 terminate("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset);
3933 long byteIndex = offset / (4*8);
3934 long bitInByte = (offset % 32) >> 2;
3935 bitmap[byteIndex] |= (1 << bitInByte);
3938 // allocate worst case size block of all slide info
3939 const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes.
3940 const unsigned toc_count = (unsigned)bitmapSize/entry_size;
3941 dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset);
3942 slideInfo->version = 1;
3943 slideInfo->toc_offset = sizeof(dyld_cache_slide_info);
3944 slideInfo->toc_count = toc_count;
3945 slideInfo->entries_offset = (slideInfo->toc_offset+2*toc_count+127)&(-128);
3946 slideInfo->entries_count = 0;
3947 slideInfo->entries_size = entry_size;
3948 // append each unique entry
3949 const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap;
3950 dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset());
3951 int entry_count = 0;
3952 for (int i=0; i < toc_count; ++i) {
3953 const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i];
3954 // see if it is same as one already added
3956 for (int j=0; j < entry_count; ++j) {
3957 if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) {
3958 slideInfo->set_toc(i, j);
3965 memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size);
3966 slideInfo->set_toc(i, entry_count++);
3969 slideInfo->entries_count = entry_count;
3970 ::free((void*)bitmap);
3972 _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2);
3978 void SharedCacheBuilder::setPointerContentV3(dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* loc
, uint64_t targetVMAddr
, size_t next
)
3980 assert(targetVMAddr
> _readExecuteRegion
.unslidLoadAddress
);
3981 assert(targetVMAddr
< _readOnlyRegion
.unslidLoadAddress
+_readOnlyRegion
.sizeInUse
);
3982 dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp
;
3986 if ( _aslrTracker
.hasAuthData(loc
, &diversity
, &hasAddrDiv
, &key
) ) {
3987 // if base cache address cannot fit into target, then use offset
3988 tmp
.arm64e
.authRebase
.target
= _readExecuteRegion
.unslidLoadAddress
;
3989 if ( tmp
.arm64e
.authRebase
.target
!= _readExecuteRegion
.unslidLoadAddress
)
3990 targetVMAddr
-= _readExecuteRegion
.unslidLoadAddress
;
3991 loc
->arm64e
.authRebase
.target
= targetVMAddr
;
3992 loc
->arm64e
.authRebase
.diversity
= diversity
;
3993 loc
->arm64e
.authRebase
.addrDiv
= hasAddrDiv
;
3994 loc
->arm64e
.authRebase
.key
= key
;
3995 loc
->arm64e
.authRebase
.next
= next
;
3996 loc
->arm64e
.authRebase
.bind
= 0;
3997 loc
->arm64e
.authRebase
.auth
= 1;
3998 assert(loc
->arm64e
.authRebase
.target
== targetVMAddr
&& "target truncated");
3999 assert(loc
->arm64e
.authRebase
.next
== next
&& "next location truncated");
4002 uint8_t highByte
= 0;
4003 _aslrTracker
.hasHigh8(loc
, &highByte
);
4004 // if base cache address cannot fit into target, then use offset
4005 tmp
.arm64e
.rebase
.target
= _readExecuteRegion
.unslidLoadAddress
;
4006 if ( tmp
.arm64e
.rebase
.target
!= _readExecuteRegion
.unslidLoadAddress
)
4007 targetVMAddr
-= _readExecuteRegion
.unslidLoadAddress
;
4008 loc
->arm64e
.rebase
.target
= targetVMAddr
;
4009 loc
->arm64e
.rebase
.high8
= highByte
;
4010 loc
->arm64e
.rebase
.next
= next
;
4011 loc
->arm64e
.rebase
.bind
= 0;
4012 loc
->arm64e
.rebase
.auth
= 0;
4013 assert(loc
->arm64e
.rebase
.target
== targetVMAddr
&& "target truncated");
4014 assert(loc
->arm64e
.rebase
.next
== next
&& "next location truncated");
4018 uint16_t SharedCacheBuilder::pageStartV3(uint8_t* pageContent
, uint32_t pageSize
, const bool bitmap
[])
4020 const int maxPerPage
= pageSize
/ 4;
4021 uint16_t result
= DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
;
4022 dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* lastLoc
= nullptr;
4023 for (int i
=0; i
< maxPerPage
; ++i
) {
4025 if ( result
== DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
) {
4026 // found first rebase location in page
4029 dyld3::MachOLoaded::ChainedFixupPointerOnDisk
* loc
= (dyld3::MachOLoaded::ChainedFixupPointerOnDisk
*)(pageContent
+ i
*4);;
4030 if ( lastLoc
!= nullptr ) {
4031 // convert vmaddr based pointers to arm64e dyld cache chains
4032 setPointerContentV3(lastLoc
, lastLoc
->raw64
, loc
- lastLoc
);
4037 if ( lastLoc
!= nullptr ) {
4038 // convert vmaddr based pointers to arm64e dyld cache chain, and mark end of chain
4039 setPointerContentV3(lastLoc
, lastLoc
->raw64
, 0);
4045 void SharedCacheBuilder::writeSlideInfoV3(const bool bitmapForAllDataRegions
[], unsigned dataPageCountForAllDataRegions
)
4047 const uint32_t pageSize
= _aslrTracker
.pageSize();
4048 const uint8_t* firstDataRegionBuffer
= firstDataRegion()->buffer
;
4049 for (uint32_t dataRegionIndex
= 0; dataRegionIndex
!= _dataRegions
.size(); ++dataRegionIndex
) {
4050 Region
& dataRegion
= _dataRegions
[dataRegionIndex
];
4051 // fprintf(stderr, "writeSlideInfoV3: %s 0x%llx->0x%llx\n", dataRegion.name.c_str(), dataRegion.cacheFileOffset, dataRegion.cacheFileOffset + dataRegion.sizeInUse);
4052 // fill in fixed info
4053 assert(dataRegion
.slideInfoFileOffset
!= 0);
4054 assert((dataRegion
.sizeInUse
% pageSize
) == 0);
4055 unsigned dataPageCount
= (uint32_t)dataRegion
.sizeInUse
/ pageSize
;
4056 dyld_cache_slide_info3
* info
= (dyld_cache_slide_info3
*)dataRegion
.slideInfoBuffer
;
4058 info
->page_size
= pageSize
;
4059 info
->page_starts_count
= dataPageCount
;
4060 info
->auth_value_add
= _archLayout
->sharedMemoryStart
;
4062 // fill in per-page starts
4063 const size_t bitmapEntriesPerPage
= (sizeof(bool)*(pageSize
/4));
4064 uint8_t* pageContent
= dataRegion
.buffer
;
4065 unsigned numPagesFromFirstDataRegion
= (uint32_t)(dataRegion
.buffer
- firstDataRegionBuffer
) / pageSize
;
4066 assert((numPagesFromFirstDataRegion
+ dataPageCount
) <= dataPageCountForAllDataRegions
);
4067 const bool* bitmapForRegion
= (const bool*)bitmapForAllDataRegions
+ (bitmapEntriesPerPage
* numPagesFromFirstDataRegion
);
4068 const bool* bitmapForPage
= bitmapForRegion
;
4069 //for (unsigned i=0; i < dataPageCount; ++i) {
4070 dispatch_apply(dataPageCount
, DISPATCH_APPLY_AUTO
, ^(size_t i
) {
4071 info
->page_starts
[i
] = pageStartV3(pageContent
+ (i
* pageSize
), pageSize
, bitmapForPage
+ (i
* bitmapEntriesPerPage
));
4074 // update region with final size
4075 dataRegion
.slideInfoFileSize
= align(__offsetof(dyld_cache_slide_info3
, page_starts
[dataPageCount
]), _archLayout
->sharedRegionAlignP2
);
4076 if ( dataRegion
.slideInfoFileSize
> dataRegion
.slideInfoBufferSizeAllocated
) {
4077 _diagnostics
.error("kernel slide info overflow buffer");
4079 // Update the mapping entry on the cache header
4080 const dyld_cache_header
* cacheHeader
= (dyld_cache_header
*)_readExecuteRegion
.buffer
;
4081 dyld_cache_mapping_and_slide_info
* slidableMappings
= (dyld_cache_mapping_and_slide_info
*)(_readExecuteRegion
.buffer
+ cacheHeader
->mappingWithSlideOffset
);
4082 slidableMappings
[1 + dataRegionIndex
].slideInfoFileSize
= dataRegion
.slideInfoFileSize
;