Loading...
--- dyld/dyld-1340/cache_builder/SectionCoalescer.cpp
+++ /dev/null
@@ -1,477 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
- *
- * Copyright (c) 2014 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@
- */
-
-#include "SectionCoalescer.h"
-#include "SubCache.h"
-
-//
-// MARK: --- CoalescedSection methods ---
-//
-
-// TODO
-
-//
-// MARK: --- CoalescedStringsSection methods ---
-//
-
-// TODO
-
-//
-// MARK: --- CoalescedGOTSection methods ---
-//
-
-void CoalescedGOTSection::addClientDylibSection(OptimizedGOTSection* section)
-{
- this->dylibSections.push_back(section);
-}
-
-std::pair<uint32_t, bool> CoalescedGOTSection::addOptimizedOffset(uint32_t pointerSize,
- CoalescedGOTSection::GOTKey key)
-{
- GOTMap& gotMap = key.isFunctionVariant ? fvTargetsToOffsets : gotTargetsToOffsets;
- uint32_t cacheSectionOffset = (uint32_t)(gotMap.size() * pointerSize);
- auto itAndInserted = gotMap.insert({ key, cacheSectionOffset });
- if ( itAndInserted.second ) {
- // We inserted the element, so its offset is already valid. Nothing else to do
- return { cacheSectionOffset, false };
- } else {
- // Debugging only. If we didn't include the GOT then we saved that many bytes
- this->savedSpace += pointerSize;
- cacheSectionOffset = itAndInserted.first->second;
-
- return { cacheSectionOffset, false };
- }
-}
-
-void CoalescedGOTSection::addFunctionVariantInfo(CoalescedGOTSection::GOTKey key,
- CoalescedGOTSection::FunctionVariantInfo info)
-{
- // store function-variant index in other map
- this->functionVariantIndexes[key] = info;
-}
-
-uint64_t CoalescedGOTSection::numSourceGOTs() const
-{
- uint64_t totalSourceGOTs = 0;
- for ( const OptimizedSection* section : dylibSections ) {
- totalSourceGOTs += section->numOptimizedEntries();
- }
- return totalSourceGOTs;
-}
-
-uint64_t CoalescedGOTSection::numCacheGOTs() const
-{
- return this->gotTargetsToOffsets.size() + fvTargetsToOffsets.size();
-}
-
-bool CoalescedGOTSection::empty() const
-{
- return this->gotTargetsToOffsets.empty() && fvTargetsToOffsets.empty();
-}
-
-uint64_t CoalescedGOTSection::gotVMSize(uint32_t pointerSize) const
-{
- return this->gotTargetsToOffsets.size() * pointerSize;
-}
-
-uint64_t CoalescedGOTSection::fvVMSize(uint32_t pointerSize) const
-{
- return this->fvTargetsToOffsets.size() * pointerSize;
-}
-
-void CoalescedGOTSection::sort(uint32_t pointerSize, std::string_view sectionName,
- std::span<OptimizedGOTSection*> dylibSections,
- bool functionVariants,
- CoalescedGOTSection::GOTMap& gotMap)
-{
- // Sort the coalesced GOTs based on the target install name. We find GOTs in the order we parse
- // the fixups in the dylibs, but we want the final cache to keep all GOTs for the same target near
- // each other
- typedef CoalescedGOTSection::GOTKey Key;
- std::vector<Key> sortedKeys;
- sortedKeys.reserve(gotMap.size());
- for ( const auto& keyAndValue : gotMap )
- sortedKeys.push_back(keyAndValue.first);
-
- std::sort(sortedKeys.begin(), sortedKeys.end(), [](const Key& a, const Key& b) {
- // sort all function-variants together at end
- if ( a.isFunctionVariant != b.isFunctionVariant )
- return b.isFunctionVariant;
- // sort first by impl dylib name
- if ( a.targetDylibName != b.targetDylibName )
- return (a.targetDylibName < b.targetDylibName);
- // if install names are the same, sort by symbol name
- return a.targetSymbolName < b.targetSymbolName;
- });
-
- // Rewrite entries from their original offset to the new offset
- std::unordered_map<uint32_t, uint32_t> oldToNewOffsetMap;
- for ( uint32_t i = 0; i != sortedKeys.size(); ++i ) {
- const Key& key = sortedKeys[i];
- auto it = gotMap.find(key);
- assert(it != gotMap.end());
-
- uint32_t newCacheSectionOffset = i * pointerSize;
-
- // Record the offset mapping for updating the dylibs
- oldToNewOffsetMap[it->second] = newCacheSectionOffset;
-
- const bool log = false;
- if ( log ) {
- printf("%s[%d]: %s\n", sectionName.data(), newCacheSectionOffset, key.targetSymbolName.data());
- }
-
- it->second = newCacheSectionOffset;
- }
-
- // Also rewrite entries in each dylib
- for ( OptimizedSection* section : dylibSections )
- section->reassignOffsets(oldToNewOffsetMap, functionVariants);
-}
-
-void CoalescedGOTSection::finalize(uint32_t pointerSize, std::string_view sectionName,
- const cache_builder::BuilderConfig& config,
- cache_builder::SubCache& subCache, cache_builder::Region& region)
-{
- CoalescedGOTSection::sort(pointerSize, sectionName, this->dylibSections, false, this->gotTargetsToOffsets);
- CoalescedGOTSection::sort(pointerSize, sectionName, this->dylibSections, true, this->fvTargetsToOffsets);
-
- if ( !this->gotTargetsToOffsets.empty() ) {
- this->gotChunk = std::make_unique<cache_builder::UniquedGOTsChunk>();
- this->gotChunk->cacheVMSize = CacheVMSize(this->gotVMSize(pointerSize));
- this->gotChunk->subCacheFileSize = CacheFileSize(this->gotVMSize(pointerSize));
-
- region.chunks.push_back(this->gotChunk.get());
- }
-
- if ( !this->fvTargetsToOffsets.empty() ) {
- this->fvChunk = std::make_unique<cache_builder::UniquedGOTsChunk>();
- this->fvChunk->cacheVMSize = CacheVMSize(this->fvVMSize(pointerSize));
- this->fvChunk->subCacheFileSize = CacheFileSize(this->fvVMSize(pointerSize));
-
- // The function variants go in TPRO
- subCache.addTPROConstChunk(config, this->fvChunk.get());
- }
-}
-
-void CoalescedGOTSection::forEachFunctionVariant(void (^callback)(const CoalescedGOTSection::FunctionVariantInfo& tv, uint64_t gotVMAddr,
- dyld3::MachOFile::PointerMetaData pmd)) const
-{
- for (const auto& fv : this->functionVariantIndexes) {
- uint32_t offsetInGOTSection = this->fvTargetsToOffsets.at(fv.first);
- uint64_t cacheVMAddr = this->fvChunk->cacheVMAddress.rawValue() + offsetInGOTSection;
- callback(fv.second, cacheVMAddr, fv.first.pmd);
- }
-}
-
-void* CoalescedGOTSection::gotLocation(CacheVMAddress gotVMAddr)
-{
- if ( gotChunk ) {
- if ( (gotVMAddr >= gotChunk->cacheVMAddress) && (gotVMAddr < (gotChunk->cacheVMAddress + gotChunk->cacheVMSize))) {
- VMOffset cacheSectionVMOffset = gotVMAddr - gotChunk->cacheVMAddress;
- return gotChunk->subCacheBuffer + cacheSectionVMOffset.rawValue();
- }
- }
-
-#if 0
- // Enable this if we ever need to write out the function variant GOTs
- if ( fvChunk ) {
- if ( (gotVMAddr >= fvChunk->cacheVMAddress) && (gotVMAddr < (fvChunk->cacheVMAddress + fvChunk->cacheVMSize))) {
- VMOffset cacheSectionVMOffset = gotVMAddr - fvChunk->cacheVMAddress;
- return fvChunk->subCacheBuffer + cacheSectionVMOffset.rawValue();
- }
- }
-#endif
-
- assert(0 && "unreachable");
-}
-
-bool CoalescedGOTSection::shouldEmitGOT(CacheVMAddress gotVMAddr) const
-{
- if ( gotChunk ) {
- if ( (gotVMAddr >= gotChunk->cacheVMAddress) && (gotVMAddr < (gotChunk->cacheVMAddress + gotChunk->cacheVMSize))) {
- return true;
- }
- }
-
- if ( fvChunk ) {
- if ( (gotVMAddr >= fvChunk->cacheVMAddress) && (gotVMAddr < (fvChunk->cacheVMAddress + fvChunk->cacheVMSize))) {
- return false;
- }
- }
-
- assert(0 && "unreachable");
-}
-
-void CoalescedGOTSection::trackFixup(void* loc)
-{
- uint64_t rawLoc = (uint64_t)loc;
- if ( gotChunk ) {
- if ( (rawLoc >= (uint64_t)gotChunk->subCacheBuffer) && (rawLoc < ((uint64_t)gotChunk->subCacheBuffer + gotChunk->cacheVMSize.rawValue()))) {
- gotChunk->tracker.add(loc);
- return;
- }
- }
-
-#if 0
- // Enable this if we ever need to write out the function variant GOTs
- if ( fvChunk ) {
- if ( (rawLoc >= (uint64_t)fvChunk->subCacheBuffer) && (rawLoc < ((uint64_t)fvChunk->subCacheBuffer + fvChunk->cacheVMSize.rawValue()))) {
- fvChunk->tracker.add(loc);
- return;
- }
- }
-#endif
-
- assert(0 && "unreachable");
-}
-
-//
-// MARK: --- DylibSectionCoalescer methods ---
-//
-
-// Returns true if the section was removed from the source dylib after being optimized
-bool DylibSectionCoalescer::sectionWasRemoved(std::string_view segmentName,
- std::string_view sectionName) const
-{
- if ( const OptimizedSection* section = this->getSection(segmentName, sectionName) ) {
- return section->sectionWasRemoved();
- }
-
- return false;
-}
-
-// Returns true if the section was optimized. It may or may not have been removed too, see sectionWasRemoved().
-bool DylibSectionCoalescer::sectionWasOptimized(std::string_view segmentName,
- std::string_view sectionName) const
-{
- if ( const OptimizedSection* section = this->getSection(segmentName, sectionName) )
- return section->sectionWasOptimized();
-
- return false;
-}
-
-OptimizedSection* DylibSectionCoalescer::getSection(std::string_view segmentName,
- std::string_view sectionName)
-{
- if (segmentName.size() > 16)
- segmentName = segmentName.substr(0, 16);
- if (sectionName.size() > 16)
- sectionName = sectionName.substr(0, 16);
-
- if ( segmentName == "__TEXT" ) {
- if ( sectionName == "__objc_classname" )
- return &this->objcClassNames;
- if ( sectionName == "__objc_methname" )
- return &this->objcMethNames;
- if ( sectionName == "__objc_methtype" )
- return &this->objcMethTypes;
- } else if ( segmentName == "__DATA_CONST" ) {
- if ( sectionName == "__got" )
- return &this->gots;
- } else if ( segmentName == "__AUTH_CONST" ) {
- if ( sectionName == "__auth_got" )
- return &this->auth_gots;
- if ( sectionName == "__auth_ptr" )
- return &this->auth_ptrs;
- }
-
- return nullptr;
-}
-
-const OptimizedSection* DylibSectionCoalescer::getSection(std::string_view segmentName,
- std::string_view sectionName) const
-{
- return ((DylibSectionCoalescer*)this)->getSection(segmentName, sectionName);
-}
-
-void DylibSectionCoalescer::forEachCacheGOTChunk(void (^callback)(const cache_builder::Chunk* cacheGOTChunk)) const
-{
- gots.forEachCacheGOTChunk(callback);
- auth_gots.forEachCacheGOTChunk(callback);
- auth_ptrs.forEachCacheGOTChunk(callback);
-}
-
-//
-// MARK: --- OptimizedSection methods ---
-//
-
-bool OptimizedSection::sectionWasRemoved() const
-{
- // Some sections, eg, GOTs, are optimized but not removed
- if ( !sectionWillBeRemoved )
- return false;
-
- return !offsetMap.empty();
-}
-
-bool OptimizedSection::sectionWasOptimized() const
-{
- return !offsetMap.empty();
-}
-
-void OptimizedSection::addUnoptimizedOffset(uint32_t sourceSectionOffset)
-{
- this->unoptimizedOffsets.insert(sourceSectionOffset);
-}
-
-void OptimizedSection::setSourceSectionInfo(const mach_o::Header::SectionInfo& info)
-{
- assert(!this->sourceSectionInfo.has_value());
- this->sourceSectionInfo = info;
-}
-
-void OptimizedSection::reassignOffsets(const std::unordered_map<uint32_t, uint32_t>& oldToNewOffsetMap,
- bool functionVariants)
-{
- for ( auto& keyAndCacheOffset : offsetMap ) {
- if ( keyAndCacheOffset.second.isFunctionVariant != functionVariants )
- continue;
- auto it = oldToNewOffsetMap.find(keyAndCacheOffset.second.cacheSectionOffset);
- assert(it != oldToNewOffsetMap.end());
- keyAndCacheOffset.second.cacheSectionOffset = it->second;
- }
-}
-
-uint64_t OptimizedSection::numOptimizedEntries() const
-{
- return this->offsetMap.size();
-}
-
-//
-// MARK: --- OptimizedStringsSection methods ---
-//
-
-void OptimizedStringSection::setSubCacheSection(CoalescedStringsSection* section)
-{
- assert(this->subCacheSection == nullptr);
- this->subCacheSection = section;
-}
-
-std::optional<uint64_t> OptimizedStringSection::cacheVMAddress(uint32_t originalDylibSectionOffset) const
-{
- auto offsetIt = offsetMap.find(originalDylibSectionOffset);
- if ( sectionWillBeRemoved ) {
- // If the section was removed then we have to find an entry for every atom in there
- assert(offsetIt != offsetMap.end());
- } else {
- // Not all GOTs are optimized, but we should find the element somewhere
- assert((offsetIt != offsetMap.end()) || unoptimizedOffsets.count(originalDylibSectionOffset));
- }
-
- if ( offsetIt == offsetMap.end() ) {
- // To was not fully optimized/coalesced so we have no element
- return std::nullopt;
- } else {
- assert(!offsetIt->second.isFunctionVariant);
- uint64_t baseVMAddr = subCacheSection->cacheChunk->cacheVMAddress.rawValue();
- return baseVMAddr + offsetIt->second.cacheSectionOffset;
- }
-}
-
-//
-// MARK: --- OptimizedGOTSection methods ---
-//
-
-void OptimizedGOTSection::setSubCacheSection(CoalescedGOTSection* section)
-{
- assert(this->subCacheSection == nullptr);
- this->subCacheSection = section;
-
- this->subCacheSection->addClientDylibSection(this);
-}
-
-std::optional<uint64_t> OptimizedGOTSection::cacheVMAddress(uint32_t originalDylibSectionOffset) const
-{
- auto offsetIt = offsetMap.find(originalDylibSectionOffset);
- if ( sectionWillBeRemoved ) {
- // If the section was removed then we have to find an entry for every atom in there
- assert(offsetIt != offsetMap.end());
- } else {
- // Not all GOTs are optimized, but we should find the element somewhere
- assert((offsetIt != offsetMap.end()) || unoptimizedOffsets.count(originalDylibSectionOffset));
- }
-
- if ( offsetIt == offsetMap.end() ) {
- // To was not fully optimized/coalesced so we have no element
- return std::nullopt;
- } else {
- uint64_t baseVMAddr = 0;
- if ( offsetIt->second.isFunctionVariant )
- baseVMAddr = subCacheSection->fvChunk->cacheVMAddress.rawValue();
- else
- baseVMAddr = subCacheSection->gotChunk->cacheVMAddress.rawValue();
- return baseVMAddr + offsetIt->second.cacheSectionOffset;
- }
-}
-
-bool OptimizedGOTSection::addOptimizedOffset(uint32_t sourceSectionOffset, uint32_t pointerSize,
- CoalescedGOTSection::GOTKey key)
-{
- auto cacheSectionOffsetAndAdded = subCacheSection->addOptimizedOffset(pointerSize, key);
-
- // Now keep track of this offset in our source dylib as pointing to this offset
- offsetMap[sourceSectionOffset] = { cacheSectionOffsetAndAdded.first, key.isFunctionVariant };
-
- return cacheSectionOffsetAndAdded.second;
-}
-
-void OptimizedGOTSection::addFunctionVariantInfo(CoalescedGOTSection::GOTKey key,
- CoalescedGOTSection::FunctionVariantInfo info)
-{
- // store function-variant index in other map
- subCacheSection->addFunctionVariantInfo(key, info);
-}
-
-OptimizedGOTSection::CoalescedGOTsMap OptimizedGOTSection::getCoalescedGOTsMap() const
-{
- if ( this->offsetMap.empty() )
- return { };
-
- assert(this->sourceSectionInfo.has_value());
- InputDylibVMAddress dylibGOTBaseVMAddr(this->sourceSectionInfo->address);
-
- OptimizedGOTSection::CoalescedGOTsMap coalescedGOTs;
- for ( const auto& dylibOffsetAndCacheOffset : this->offsetMap ) {
- VMOffset dylibSectionOffset((uint64_t)dylibOffsetAndCacheOffset.first);
- VMOffset cacheSectionOffset((uint64_t)dylibOffsetAndCacheOffset.second.cacheSectionOffset);
- if ( dylibOffsetAndCacheOffset.second.isFunctionVariant )
- coalescedGOTs[dylibGOTBaseVMAddr + dylibSectionOffset] = { this->subCacheSection->fvChunk.get(), cacheSectionOffset };
- else
- coalescedGOTs[dylibGOTBaseVMAddr + dylibSectionOffset] = { this->subCacheSection->gotChunk.get(), cacheSectionOffset };
- }
- return coalescedGOTs;
-}
-
-void OptimizedGOTSection::forEachCacheGOTChunk(void (^callback)(const cache_builder::Chunk* cacheGOTChunk)) const
-{
- if ( this->subCacheSection == nullptr )
- return;
-
- if ( this->subCacheSection->gotChunk != nullptr )
- callback(this->subCacheSection->gotChunk.get());
-
- if ( this->subCacheSection->fvChunk != nullptr )
- callback(this->subCacheSection->fvChunk.get());
-}