2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 #ifndef DyldSharedCache_h
26 #define DyldSharedCache_h
28 #include <TargetConditionals.h>
29 #include <uuid/uuid.h>
31 #if (BUILDING_LIBDYLD || BUILDING_DYLD)
32 #include <sys/types.h>
35 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
39 #include <unordered_map>
40 #include <unordered_set>
43 #include "dyld_cache_format.h"
44 #include "Diagnostics.h"
45 #include "MachOAnalyzer.h"
53 class VIS_HIDDEN DyldSharedCache
57 #if BUILDING_CACHE_BUILDER
58 enum CodeSigningDigestMode
65 enum class LocalSymbolsMode
{
73 std::string outputFilePath
;
74 std::string outputMapFilePath
;
75 const dyld3::GradedArchs
* archs
;
76 dyld3::Platform platform
;
77 LocalSymbolsMode localSymbolMode
;
79 bool optimizeDyldDlopens
;
80 bool optimizeDyldLaunches
;
81 CodeSigningDigestMode codeSigningDigestMode
;
82 bool dylibsRemovedDuringMastering
;
83 bool inodesAreSameAsRuntime
;
84 bool cacheSupportsASLR
;
86 bool isLocallyBuiltCache
;
88 bool evictLeafDylibsOnOverflow
;
89 std::unordered_map
<std::string
, unsigned> dylibOrdering
;
90 std::unordered_map
<std::string
, unsigned> dirtyDataSegmentOrdering
;
91 dyld3::json::Node objcOptimizations
;
92 std::string loggingPrefix
;
98 : mh(nullptr), length(0), isSetUID(false), protectedBySIP(false), sliceFileOffset(0), modTime(0), inode(0) { }
99 MappedMachO(const std::string
& path
, const dyld3::MachOAnalyzer
* p
, size_t l
, bool isu
, bool sip
, uint64_t o
, uint64_t m
, uint64_t i
)
100 : runtimePath(path
), mh(p
), length(l
), isSetUID(isu
), protectedBySIP(sip
), sliceFileOffset(o
), modTime(m
), inode(i
) { }
102 std::string runtimePath
;
103 const dyld3::MachOAnalyzer
* mh
;
105 uint64_t isSetUID
: 1,
107 sliceFileOffset
: 62;
108 uint64_t modTime
; // only recorded if inodesAreSameAsRuntime
109 uint64_t inode
; // only recorded if inodesAreSameAsRuntime
114 std::string errorMessage
;
115 std::set
<std::string
> warnings
;
116 std::set
<const dyld3::MachOAnalyzer
*> evictions
;
117 bool agileSignature
= false;
118 std::string cdHashFirst
;
119 std::string cdHashSecond
;
125 std::string realPath
;
126 std::string aliasPath
;
130 // This function verifies the set of dylibs that will go into the cache are self contained. That the depend on no dylibs
131 // outset the set. It will call back the loader function to try to find any mising dylibs.
132 static bool verifySelfContained(std::vector
<MappedMachO
>& dylibsToCache
,
133 std::unordered_set
<std::string
>& badZippered
,
134 MappedMachO (^loader
)(const std::string
& runtimePath
, Diagnostics
& diag
), std::vector
<std::pair
<DyldSharedCache::MappedMachO
, std::set
<std::string
>>>& excluded
);
138 // This function is single threaded and creates a shared cache. The cache file is created in-memory.
141 // options: various per-platform flags
142 // dylibsToCache: a list of dylibs to include in the cache
143 // otherOsDylibs: a list of other OS dylibs and bundle which should have load info added to the cache
144 // osExecutables: a list of main executables which should have closures created in the cache
148 // cacheContent: start of the allocated cache buffer which must be vm_deallocated after the caller writes out the buffer.
149 // cacheLength: size of the allocated cache buffer
150 // cdHash: hash of the code directory of the code blob of the created cache
151 // warnings: all warning messsages generated during the creation of the cache
154 // cacheContent: nullptr
155 // errorMessage: the string describing why the cache could not be created
156 // warnings: all warning messsages generated before the failure
158 static CreateResults
create(const CreateOptions
& options
,
159 const dyld3::closure::FileSystem
& fileSystem
,
160 const std::vector
<MappedMachO
>& dylibsToCache
,
161 const std::vector
<MappedMachO
>& otherOsDylibs
,
162 const std::vector
<MappedMachO
>& osExecutables
);
166 // Returns a text "map" file as a big string
168 std::string
mapFile() const;
170 #endif // BUILDING_CACHE_BUILDER
174 // Returns the architecture name of the shared cache, e.g. "arm64"
176 const char* archName() const;
180 // Returns the platform the cache is for
182 dyld3::Platform
platform() const;
186 // Iterates over each dylib in the cache
188 void forEachImage(void (^handler
)(const mach_header
* mh
, const char* installName
)) const;
192 // Searches cache for dylib with specified path
194 bool hasImagePath(const char* dylibPath
, uint32_t& imageIndex
) const;
198 // Is this path (which we know is in the shared cache), overridable
200 bool isOverridablePath(const char* dylibPath
) const;
204 // Path is to a dylib in the cache and this is an optimized cache so that path cannot be overridden
206 bool hasNonOverridablePath(const char* dylibPath
) const;
210 // Check if shared cache contains local symbols info
212 const bool hasLocalSymbolsInfo() const;
216 // Get code signature mapped address
218 uint64_t getCodeSignAddress() const;
222 // Searches cache for dylib with specified mach_header
224 bool findMachHeaderImageIndex(const mach_header
* mh
, uint32_t& imageIndex
) const;
227 // Iterates over each dylib in the cache
229 void forEachImageEntry(void (^handler
)(const char* path
, uint64_t mTime
, uint64_t inode
)) const;
233 // Get image entry from index
235 const mach_header
* getIndexedImageEntry(uint32_t index
, uint64_t& mTime
, uint64_t& node
) const;
238 // iterates over all dylibs and aliases
239 void forEachDylibPath(void (^handler
)(const char* dylibPath
, uint32_t index
)) const;
243 // Get image path from index
245 const char* getIndexedImagePath(uint32_t index
) const;
249 // Get the canonical (dylib) path for a given path, which may be a symlink to something in the cache
251 const char* getCanonicalPath(const char* path
) const;
255 // Iterates over each text segment in the cache
257 void forEachImageTextSegment(void (^handler
)(uint64_t loadAddressUnslid
, uint64_t textSegmentSize
, const uuid_t dylibUUID
, const char* installName
, bool& stop
)) const;
261 // Iterates over each of the three regions in the cache
263 void forEachRegion(void (^handler
)(const void* content
, uint64_t vmAddr
, uint64_t size
,
264 uint32_t initProt
, uint32_t maxProt
, uint64_t flags
)) const;
268 // Get local symbols nlist entries
270 const void* getLocalNlistEntries() const;
274 // Get local symbols nlist count
276 const uint32_t getLocalNlistCount() const;
280 // Get local symbols strings
282 const char* getLocalStrings() const;
286 // Get local symbols strings size
288 const uint32_t getLocalStringsSize() const;
292 // Iterates over each local symbol entry in the cache
294 void forEachLocalSymbolEntry(void (^handler
)(uint32_t dylibOffset
, uint32_t nlistStartIndex
, uint32_t nlistCount
, bool& stop
)) const;
297 // Returns if an address range is in this cache, and if so if in a read-only area
299 bool inCache(const void* addr
, size_t length
, bool& readOnly
) const;
302 // Returns true if a path is an alternate path (symlink)
304 bool isAlias(const char* path
) const;
307 // returns address the cache would load at if unslid
309 uint64_t unslidLoadAddress() const;
313 // returns UUID of cache
315 void getUUID(uuid_t uuid
) const;
319 // returns the vm size required to map cache
321 uint64_t mappedSize() const;
325 // searches cache for pre-built closure for program
327 const dyld3::closure::LaunchClosure
* findClosure(const char* executablePath
) const;
331 // iterates all pre-built closures for program
333 void forEachLaunchClosure(void (^handler
)(const char* executableRuntimePath
, const dyld3::closure::LaunchClosure
* closure
)) const;
337 // iterates all pre-built Image* for OS dylibs/bundles not in dyld cache
339 void forEachDlopenImage(void (^handler
)(const char* runtimePath
, const dyld3::closure::Image
* image
)) const;
343 // returns the ImageArray pointer to Images in dyld shared cache
345 const dyld3::closure::ImageArray
* cachedDylibsImageArray() const;
349 // returns the ImageArray pointer to Images in OS with pre-build dlopen closure
351 const dyld3::closure::ImageArray
* otherOSImageArray() const;
355 // searches cache for pre-built dlopen closure for OS dylib/bundle
357 const dyld3::closure::Image
* findDlopenOtherImage(const char* path
) const;
360 // Returns the pointer to the slide info for this cache
362 const dyld_cache_slide_info
* legacyCacheSlideInfo() const;
365 // Returns a pointer to the __DATA region mapping in the cache
367 const dyld_cache_mapping_info
* legacyCacheDataRegionMapping() const;
370 // Returns a pointer to the start of the __DATA region in the cache
372 const uint8_t* legacyCacheDataRegionBuffer() const;
375 // Returns a pointer to the shared cache optimized Objective-C data structures
377 const objc_opt::objc_opt_t
* objcOpt() const;
380 // Returns a pointer to the shared cache optimized Objective-C pointer structures
382 const void* objcOptPtrs() const;
384 // Returns true if the cache has any slide info, either old style on a single data region
385 // or on each individual data mapping
386 bool hasSlideInfo() const;
388 void forEachSlideInfo(void (^handler
)(uint64_t mappingStartAddress
, uint64_t mappingSize
,
389 const uint8_t* mappingPagesStart
,
390 uint64_t slideInfoOffset
, uint64_t slideInfoSize
,
391 const dyld_cache_slide_info
* slideInfoHeader
)) const;
395 // returns true if the offset is in the TEXT of some cached dylib and sets *index to the dylib index
397 bool addressInText(uint32_t cacheOffset
, uint32_t* index
) const;
399 uint32_t patchableExportCount(uint32_t imageIndex
) const;
400 void forEachPatchableExport(uint32_t imageIndex
, void (^handler
)(uint32_t cacheOffsetOfImpl
, const char* exportName
)) const;
401 void forEachPatchableUseOfExport(uint32_t imageIndex
, uint32_t cacheOffsetOfImpl
,
402 void (^handler
)(dyld_cache_patchable_location patchLocation
)) const;
404 // Helper to get the addend for a patch location since we don't want to put C++ in the shared cache format header
405 static uint64_t getAddend(const dyld_cache_patchable_location
& loc
) {
406 uint64_t unsingedAddend
= loc
.addend
;
407 int64_t signedAddend
= (int64_t)unsingedAddend
;
408 signedAddend
= (signedAddend
<< 52) >> 52;
409 return (uint64_t)signedAddend
;
411 // Helper to get the key nam for a patch location since we don't want to put C++ in the shared cache format header
412 static const char* keyName(const dyld_cache_patchable_location
& patchLocation
) {
413 dyld3::MachOLoaded::ChainedFixupPointerOnDisk dummy
;
414 dummy
.arm64e
.authRebase
.auth
= 1;
415 dummy
.arm64e
.authRebase
.bind
= 0;
416 dummy
.arm64e
.authRebase
.key
= patchLocation
.key
;
417 return dummy
.arm64e
.keyName();
420 #if (BUILDING_LIBDYLD || BUILDING_DYLD)
422 typedef void (*DataConstLogFunc
)(const char*, ...) __attribute__((format(printf
, 1, 2)));
423 void changeDataConstPermissions(mach_port_t machTask
, uint32_t permissions
, DataConstLogFunc logFunc
) const;
425 struct DataConstLazyScopedWriter
{
426 DataConstLazyScopedWriter(const DyldSharedCache
* cache
, mach_port_t machTask
, DataConstLogFunc logFunc
);
427 ~DataConstLazyScopedWriter();
429 // Delete all other kinds of constructors to make sure we don't accidentally copy these around
430 DataConstLazyScopedWriter() = delete;
431 DataConstLazyScopedWriter(const DataConstLazyScopedWriter
&) = delete;
432 DataConstLazyScopedWriter(DataConstLazyScopedWriter
&&) = delete;
433 DataConstLazyScopedWriter
& operator=(const DataConstLazyScopedWriter
&) = delete;
434 DataConstLazyScopedWriter
& operator=(DataConstLazyScopedWriter
&&) = delete;
436 void makeWriteable();
438 const DyldSharedCache
* cache
= nullptr;
439 mach_port_t machTask
= MACH_PORT_NULL
;
440 DataConstLogFunc logFunc
= nullptr;
441 bool wasMadeWritable
= false;
444 struct DataConstScopedWriter
{
445 DataConstScopedWriter(const DyldSharedCache
* cache
, mach_port_t machTask
, DataConstLogFunc logFunc
);
446 ~DataConstScopedWriter() = default;
448 // Delete all other kinds of constructors to make sure we don't accidentally copy these around
449 DataConstScopedWriter() = delete;
450 DataConstScopedWriter(const DataConstScopedWriter
&) = delete;
451 DataConstScopedWriter(DataConstScopedWriter
&&) = delete;
452 DataConstScopedWriter
& operator=(const DataConstScopedWriter
&) = delete;
453 DataConstScopedWriter
& operator=(DataConstScopedWriter
&&) = delete;
455 DataConstLazyScopedWriter writer
;
459 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
460 // MRM map file generator
461 std::string
generateJSONMap(const char* disposition
) const;
463 // This generates a JSON representation of deep reverse dependency information in the cache.
464 // For each dylib, the output will contain the list of all the other dylibs transitively
465 // dependening on that library. (For example, the entry for libsystem will contain almost
466 // all of the dylibs in the cache ; a very high-level framework such as ARKit will have way
467 // fewer dependents).
468 // This is used by the shared cache ordering script to put "deep" dylibs used by everybody
469 // closer to the center of the cache.
470 std::string
generateJSONDependents() const;
473 // Note these enum entries are only valid for 64-bit archs.
474 enum class ConstantClasses
{
475 cfStringAtomSize
= 32
478 // Returns the start and size of the range in the shared cache of the ObjC constants, such as
479 // all of the CFString's which have been moved in to a contiguous range
480 std::pair
<const void*, uint64_t> getObjCConstantRange() const;
482 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
483 dyld3::MachOAnalyzer::VMAddrConverter
makeVMAddrConverter(bool contentRebased
) const;
486 dyld_cache_header header
;
488 // The most mappings we could generate.
489 // For now its __TEXT, __DATA_CONST, __DATA_DIRTY, __DATA, __LINKEDIT,
490 // and optionally also __AUTH, __AUTH_CONST, __AUTH_DIRTY
491 static const uint32_t MaxMappings
= 8;
494 // Returns a variable of type "const T" which corresponds to the header field with the given unslid address
496 const T
getAddrField(uint64_t addr
) const;
498 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
499 void fillMachOAnalyzersMap(std::unordered_map
<std::string
,dyld3::MachOAnalyzer
*> & dylibAnalyzers
) const;
500 void computeReverseDependencyMapForDylib(std::unordered_map
<std::string
, std::set
<std::string
>> &reverseDependencyMap
, const std::unordered_map
<std::string
,dyld3::MachOAnalyzer
*> & dylibAnalyzers
, const std::string
&loadPath
) const;
501 void computeReverseDependencyMap(std::unordered_map
<std::string
, std::set
<std::string
>> &reverseDependencyMap
) const;
502 void findDependentsRecursively(std::unordered_map
<std::string
, std::set
<std::string
>> &transitiveDependents
, const std::unordered_map
<std::string
, std::set
<std::string
>> &reverseDependencyMap
, std::set
<std::string
> & visited
, const std::string
&loadPath
) const;
503 void computeTransitiveDependents(std::unordered_map
<std::string
, std::set
<std::string
>> & transitiveDependents
) const;
514 #endif /* DyldSharedCache_h */