/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
*
* Copyright (c) 2017 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#ifndef SubCache_hpp
#define SubCache_hpp
#include "Defines.h"
#include "Chunk.h"
#include "MachOFile.h"
#include "Optimizers.h"
#include "Platform.h"
#include "Types.h"
#include <uuid/uuid.h>
#include <vector>
namespace cache_builder
{
struct BuilderOptions;
struct Region
{
// Note the order of this enum is the order in the final cache binary.
// It is sorted to try keep page tables to a minimum, ie, keeping similar regions adjacent
enum class Kind : uint32_t
{
text,
// Rosetta expects __DATA_CONST after __TEXT, as we currently sort using this enum
dataConst,
// Put TPRO const before data as we want it to be adjacent to the DATA_DIRTY which it at the start of data
tproConst,
data,
// Put TPRO auth const before auth as we want it to be adjacent to the DATA_DIRTY which it at the start of auth
tproAuthConst,
auth,
// FIXME: Move this to be after DATA_CONST to reduce page tables
// Needs rdar://96315050
authConst,
readOnly,
linkedit,
unmapped,
dynamicConfig,
codeSignature,
numKinds
};
Region(Kind kind) : kind(kind) { }
uint32_t initProt() const;
uint32_t maxProt() const;
bool canContainAuthPointers() const;
bool needsSharedCacheMapping() const;
bool needsSharedCacheReserveAddressSpace() const;
bool needsRegionPadding(const Region& next) const;
Kind kind;
// The chunks from dylibs, optimzations, etc, which make up this Region
std::vector<Chunk*> chunks;
std::list<AlignChunk> alignmentChunks;
CacheFileOffset subCacheFileOffset;
CacheFileSize subCacheFileSize;
CacheVMAddress subCacheVMAddress;
CacheVMSize subCacheVMSize;
uint8_t* subCacheBuffer = nullptr;
};
struct SubCache
{
private:
enum class Kind
{
mainDevelopment,
stubsDevelopment,
mainCustomer,
stubsCustomer,
// If we aren't the main cache, or a stubs cache, then the remainder is a universal "sub" cache
// which is typically TEXT/DATA/LINKEDIT
subUniversal,
symbols
};
SubCache(Kind kind);
public:
SubCache() = delete;
~SubCache() = default;
SubCache(const SubCache&) = delete;
SubCache& operator=(const SubCache&) = delete;
SubCache(SubCache&&) = default;
SubCache& operator=(SubCache&&) = default;
// Helper methods to build the various kinds of subCache
static SubCache makeMainCache(const BuilderOptions& options, bool isDevelopment);
static SubCache makeSubCache(const BuilderOptions& options);
static SubCache makeStubsCache(const BuilderOptions& options, bool isDevelopment);
static SubCache makeSymbolsCache();
// These methods are called by computeSubCaches() to add Chunk's to the subCache
void addDylib(const BuilderConfig& config, CacheDylib& cacheDylib);
void addLinkeditFromDylib(CacheDylib& cacheDylib);
void addCacheHeaderChunk(const BuilderOptions& options, const BuilderConfig& config,
const std::span<CacheDylib> cacheDylibs);
void addObjCHeaderInfoReadWriteChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer);
void addCodeSignatureChunk();
void addObjCOptsHeaderChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer);
void addObjCHeaderInfoReadOnlyChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer);
void addObjCImageInfoChunk(const BuilderConfig& config, ObjCOptimizer& objcOptimizer);
void addObjCSelectorStringsChunk(const BuilderConfig& config, ObjCSelectorOptimizer& objCSelectorOptimizer);
void addObjCSelectorHashTableChunk(const BuilderConfig& config, ObjCSelectorOptimizer& objCSelectorOptimizer);
void addObjCClassNameStringsChunk(const BuilderConfig& config, ObjCClassOptimizer& objcClassOptimizer);
void addObjCClassHashTableChunk(const BuilderConfig& config, ObjCClassOptimizer& objcClassOptimizer);
void addObjCProtocolNameStringsChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer);
void addObjCProtocolHashTableChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer);
void addObjCProtocolSwiftDemangledNamesChunk(const BuilderConfig& config, ObjCProtocolOptimizer& objcProtocolOptimizer);
void addObjCCanonicalProtocolsChunk(const BuilderConfig& config,
ObjCProtocolOptimizer& objcProtocolOptimizer);
void addObjCCategoriesChunk(const BuilderConfig& config,
ObjCCategoryOptimizer& objcCategoryOptimizer);
void addObjCIMPCachesChunk(const BuilderConfig& config, ObjCIMPCachesOptimizer& objcIMPCachesOptimizer);
void addCacheTrieChunk(DylibTrieOptimizer& dylibTrieOptimizer);
void addPatchTableChunk(PatchTableOptimizer& patchTableOptimizer);
void addFunctionVariantsChunk(FunctionVariantsOptimizer& optimizer);
void addCacheDylibsLoaderChunk(PrebuiltLoaderBuilder& builder);
void addExecutableLoaderChunk(PrebuiltLoaderBuilder& builder);
void addExecutablesTrieChunk(PrebuiltLoaderBuilder& builder);
void addSwiftOptsHeaderChunk(const BuilderConfig& config, SwiftOptimizer& opt);
void addSwiftTypeHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt);
void addSwiftMetadataHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt);
void addSwiftForeignHashTableChunk(const BuilderConfig& config, SwiftOptimizer& opt);
void addSwiftPrespecializedMetadataPointerTableChunks(const BuilderConfig& config, SwiftOptimizer& opt);
void addPrewarmingDataChunk(const BuilderConfig& config, PrewarmingOptimizer& opt);
void addUnmappedSymbols(const BuilderConfig& config, UnmappedSymbolsOptimizer& opt);
void addDynamicConfigChunk();
void addSlideInfoChunks();
void removeEmptyRegions();
// When "kind == sub", sets the suffix on this subCache
// This has to be done after creating things like stubs sub caches, which might move the indices
void setSuffix(mach_o::Platform platform, bool forceDevelopmentSubCacheSuffix,
size_t subCacheIndex);
void setCodeSignatureSize(const BuilderOptions& options, const BuilderConfig& config,
CacheFileSize estimatedSize);
error::Error computeSlideInfo(const BuilderConfig& config);
// Emits a dyld_cache_header for this subCache
void writeCacheHeader(const BuilderOptions& options, const BuilderConfig& config,
const std::span<CacheDylib> cacheDylibs,
uint32_t osVersion, uint32_t altPlatform, uint32_t altOsVersion);
// Adds any additional fields which are set only on the main subCache(s)
void addMainCacheHeaderInfo(const BuilderOptions& options, const BuilderConfig& config,
const std::span<CacheDylib> cacheDylibs,
CacheVMSize totalVMSize, uint64_t maxSlide,
CacheVMAddress dyldInCacheUnslidAddr,
CacheVMAddress dyldInCacheEntryUnslidAddr,
const DylibTrieOptimizer& dylibTrieOptimizer,
const ObjCOptimizer& objcOpt,
const SwiftOptimizer& swiftOpt,
const PatchTableOptimizer& patchTableOptimizer,
const FunctionVariantsOptimizer& functionVariantOptimizer,
const PrebuiltLoaderBuilder& prebuiltLoaderBuilder,
const PrewarmingOptimizer& prewarmingOptimizer);
// Adds any additional fields which are set only on the .symbols subCache
void addSymbolsCacheHeaderInfo(const UnmappedSymbolsOptimizer& unmappedSymbolsOptimizer);
void codeSign(Diagnostics& diag, const BuilderOptions& options, const BuilderConfig& config);
bool isMainCache() const;
bool isMainDevelopmentCache() const;
bool isMainCustomerCache() const;
bool isSubCache() const;
bool isStubsCache() const;
bool isStubsDevelopmentCache() const;
bool isStubsCustomerCache() const;
bool isSymbolsCache() const;
void addStubsChunk(Chunk* chunk);
bool shouldKeepCache(bool keepDevelopmentCaches, bool keepCustomerCaches) const;
// Note this is for x86_64 only, and works out where the TPRO "regions" are inside the DATA region
static void forEachTPRORegionInData(SubCache* mainSubCache, std::span<SubCache*> subCaches,
void (^callback)(Region& region, const Chunk* firstChunk, const Chunk* lastChunk));
// Adds the given chunk to the given region
void addTextChunk(Chunk* chunk);
void addDataChunk(Chunk* chunk);
void addTPROConstChunk(const BuilderConfig& config, Chunk* chunk);
void addDataConstChunk(Chunk* chunk);
void addAuthChunk(Chunk* chunk);
void addAuthConstChunk(Chunk* chunk);
void addReadOnlyChunk(const BuilderConfig& config, Chunk* chunk);
void addLinkeditChunk(Chunk* chunk);
void addUnmappedChunk(Chunk* chunk);
void addCodeSignatureChunk(Chunk* chunk);
void addObjCReadWriteChunk(const BuilderConfig& config, Chunk* chunk);
#if BUILDING_CACHE_BUILDER_UNIT_TESTS
// We need everything public to write tests
public:
#else
private:
#endif
// Returns true if the cache header on this subCache needs an image list
// The symbols cache and stubs caches, for example, don't need this
bool needsCacheHeaderImageList(const BuilderOptions& options) const;
// Add image info to the subCache header, if it needs it
void addCacheHeaderImageInfo(const BuilderOptions& options,
const BuilderConfig& config,
const std::span<CacheDylib> cacheDylibs);
static uint64_t getCacheType(const BuilderOptions& options);
uint32_t getCacheSubType() const;
void writeCacheHeaderMappings();
static error::Error convertChainsToVMAddresses(const BuilderConfig& config, Region& region);
static error::Error computeSlideInfoV1(const BuilderConfig& config,
SlideInfoChunk* slideChunk,
Region& region);
static error::Error computeSlideInfoV2(const BuilderConfig& config,
SlideInfoChunk* slideChunk,
Region& region);
static error::Error computeSlideInfoV3(const BuilderConfig& config,
SlideInfoChunk* slideChunk,
Region& region);
static error::Error computeSlideInfoV5(const BuilderConfig& config,
SlideInfoChunk* slideChunk,
Region& region);
static error::Error computeSlideInfoForRegion(const BuilderConfig& config,
SlideInfoChunk* slideChunk,
Region& region);
public:
Kind kind;
std::vector<Region> regions;
// This buffer is vm_allocated (or points to a file on disk). Either way, the builder
// creates it but doesn't destroy it. Its ownership will move out to the calling code via
// getResults(), and the caller will deallocate/unmap as needed
uint8_t* buffer = nullptr;
uint64_t bufferSize = 0;
CacheVMAddress subCacheVMAddress;
uint8_t cdHash[20];
uint8_t agilecdHash[20]; // if using agile signatures, this is the sha256
uuid_string_t uuidString;
std::string fileSuffix;
// Some Chunk instances are owned by the SubCache. Eg, it owns its own header
std::unique_ptr<CacheHeaderChunk> cacheHeader;
std::unique_ptr<SlideInfoChunk> tproConstSlideInfo;
std::unique_ptr<SlideInfoChunk> dataSlideInfo;
std::unique_ptr<SlideInfoChunk> dataConstSlideInfo;
std::unique_ptr<SlideInfoChunk> tproAuthConstSlideInfo;
std::unique_ptr<SlideInfoChunk> authSlideInfo;
std::unique_ptr<SlideInfoChunk> authConstSlideInfo;
std::unique_ptr<CodeSignatureChunk> codeSignature;
std::unique_ptr<ObjCOptsHeaderChunk> objcOptsHeader;
std::unique_ptr<ObjCHeaderInfoReadOnlyChunk> objcHeaderInfoRO;
std::unique_ptr<ObjCHeaderInfoReadWriteChunk> objcHeaderInfoRW;
std::unique_ptr<ObjCImageInfoChunk> objcImageInfo;
std::unique_ptr<ObjCStringsChunk> objcSelectorStrings;
std::unique_ptr<ObjCSelectorHashTableChunk> objcSelectorsHashTable;
std::unique_ptr<ObjCStringsChunk> objcClassNameStrings;
std::unique_ptr<ObjCClassHashTableChunk> objcClassesHashTable;
std::unique_ptr<ObjCStringsChunk> objcProtocolNameStrings;
std::unique_ptr<ObjCProtocolHashTableChunk> objcProtocolsHashTable;
std::unique_ptr<ObjCCanonicalProtocolsChunk> objcCanonicalProtocols;
std::unique_ptr<ObjCPreAttachedCategoriesChunk> objcCategories;
std::unique_ptr<ObjCStringsChunk> objcSwiftDemangledNameStrings;
std::unique_ptr<ObjCIMPCachesChunk> objcIMPCaches;
std::unique_ptr<SwiftOptsHeaderChunk> swiftOptsHeader;
std::unique_ptr<SwiftProtocolConformancesHashTableChunk> swiftTypeHashTable;
std::unique_ptr<SwiftProtocolConformancesHashTableChunk> swiftMetadataHashTable;
std::unique_ptr<SwiftProtocolConformancesHashTableChunk> swiftForeignTypeHashTable;
std::unique_ptr<CacheTrieChunk> cacheDylibsTrie;
std::unique_ptr<PatchTableChunk> patchTable;
std::unique_ptr<FunctionVariantsPatchTableChunk> functionVariants;
std::unique_ptr<DynamicConfigChunk> dynamicConfig;
std::unique_ptr<PrebuiltLoaderChunk> cacheDylibsLoaders;
std::unique_ptr<PrebuiltLoaderChunk> executableLoaders;
std::unique_ptr<CacheTrieChunk> executablesTrie;
std::unique_ptr<SymbolStringsChunk> optimizedSymbolStrings;
std::vector<std::unique_ptr<PointerHashTableChunk>> pointerHashTables;
std::unique_ptr<PrewarmingChunk> prewarmingChunk;
// Each subCache has its own Linkedit so needs its own optimizer
SymbolStringsOptimizer symbolStringsOptimizer;
// Each subCache can have its own uniqued GOTs
UniquedGOTsOptimizer uniquedGOTsOptimizer;
// FIXME: Should these be zero-sized Chunk's instead?
uint64_t rosettaReadOnlyAddr = 0;
uint64_t rosettaReadOnlySize = 0;
uint64_t rosettaReadWriteAddr = 0;
uint64_t rosettaReadWriteSize = 0;
// The following is only used depending on the kind field
// Main sub caches have a list of the other sub caches (indices are in to the builder subCaches array)
std::vector<SubCache*> subCaches;
};
} // namespace cache_builder
#endif /* SubCache_hpp */