Loading...
--- dyld/dyld-960/cache-builder/OptimizerObjC.cpp
+++ dyld/dyld-1125.5/cache-builder/OptimizerObjC.cpp
@@ -173,7 +173,7 @@
pint_t getVMAddress(pint_t index) const {
if ( index >= _count ) {
- _cache->diagnostics().error("index out of range in section (%s)", _section->sectname());
+ _cache->diagnostics().error("index out of range in section %s", _section->sectname());
return 0;
}
return (pint_t)P::getP(_base[index]);
@@ -189,7 +189,7 @@
void setVMAddress(pint_t index, pint_t value) {
if ( index >= _count ) {
- _cache->diagnostics().error("index out of range in section (%s)", _section->sectname());
+ _cache->diagnostics().error("index out of range in section %s", _section->sectname());
return;
}
P::setP(_base[index], value);
@@ -234,7 +234,7 @@
T& get(uint64_t index) const {
if (index >= _count) {
- _cache->diagnostics().error("index out of range in section (%s)", _section->sectname());
+ _cache->diagnostics().error("index out of range in section %s", _section->sectname());
}
return _base[index];
}
@@ -273,10 +273,9 @@
return (pint_t)element->second;
}
- void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) {
- const CacheBuilder::CacheCoalescedText::StringSection& methodNames = coalescedText.getSectionData("__objc_methname");
- for (const auto& stringAndOffset : methodNames.stringsToOffsets) {
- uint64_t vmAddr = methodNames.bufferVMAddr + stringAndOffset.second;
+ void visitCoalescedStrings(const CacheBuilder::CoalescedStringsSection& coalescedMethodNames) {
+ for (const auto& stringAndOffset : coalescedMethodNames.stringsToOffsets) {
+ uint64_t vmAddr = coalescedMethodNames.bufferVMAddr + stringAndOffset.second;
_selectorStrings[stringAndOffset.first.data()] = vmAddr;
}
}
@@ -308,10 +307,9 @@
ClassListBuilder(const std::unordered_map<const macho_header<P>*, uint16_t>& dylibIndices) : _dylibIndices(dylibIndices) { }
- void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) {
- const CacheBuilder::CacheCoalescedText::StringSection& classNames = coalescedText.getSectionData("__objc_classname");
- for (const auto& stringAndOffset : classNames.stringsToOffsets) {
- uint64_t vmAddr = classNames.bufferVMAddr + stringAndOffset.second;
+ void visitCoalescedStrings(const CacheBuilder::CoalescedStringsSection& coalescedClassNames) {
+ for (const auto& stringAndOffset : coalescedClassNames.stringsToOffsets) {
+ uint64_t vmAddr = coalescedClassNames.bufferVMAddr + stringAndOffset.second;
_uniquedClassNames[stringAndOffset.first.data()] = vmAddr;
}
}
@@ -466,7 +464,7 @@
T inlinedSelectorsVMAddrEnd;
};
-template <typename P>
+template <typename P, typename H>
class IMPCachesEmitter
{
typedef typename P::uint_t pint_t;
@@ -489,30 +487,11 @@
std::map<std::string_view, const macho_header<P>*> _dylibs;
const std::vector<const IMPCaches::Selector*> inlinedSelectors;
- struct ImpCacheHeader {
- int32_t fallback_class_offset;
- uint32_t cache_shift : 5;
- uint32_t cache_mask : 11;
- uint32_t occupied : 14;
- uint32_t has_inlines : 1;
- uint32_t bit_one : 1;
- };
-
- struct ImpCacheEntry_v1 {
- uint32_t selOffset;
- uint32_t impOffset;
- };
-
- struct ImpCacheEntry_v2 {
- int64_t impOffset : 38;
- uint64_t selOffset : 26;
- };
-
public:
static size_t sizeForImpCacheWithCount(int entries) {
static_assert(sizeof(ImpCacheEntry_v1) == sizeof(ImpCacheEntry_v2));
- return sizeof(ImpCacheHeader) + entries * sizeof(ImpCacheEntry_v1);
+ return sizeof(H) + entries * sizeof(ImpCacheEntry_v1);
}
struct ImpCacheContents {
@@ -650,6 +629,7 @@
cachesFormatVersion(cachesVersion), objcASLRTracker(objcASLRTracker) {
for (const SharedCacheBuilder::DylibInfo& d : dylibInfos) {
_dylibInfos[d.dylibID] = &d;
+ _dylibInfos[d.input->mappedFile.mh->installName()] = &d;
}
for (const macho_header<P>* d : dylibs) {
const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*) d;
@@ -684,7 +664,7 @@
const char* className = cls->getName(cache);
if (cls->getVTable(cache) != 0) {
- diag.error("Class (%s) has non-zero vtable\n", className);
+ diag.error("Class '%s' has non-zero vtable\n", className);
return;
}
@@ -717,20 +697,12 @@
printf("Writing cache for %sclass %s (%#08llx)\n", cls->isMetaClass(cache) ? "meta" : "", className, clsVMAddr);
}
- struct ImpCacheHeader {
- int32_t fallback_class_offset;
- uint32_t cache_shift : 5;
- uint32_t cache_mask : 11;
- uint32_t occupied : 14;
- uint32_t has_inlines : 1;
- uint32_t bit_one : 1;
- };
pint_t* vtableAddr = cls->getVTableAddress();
// the alignment of ImpCaches to 16 bytes is only needed for arm64_32.
- ImpCacheHeader* cachePtr = (ImpCacheHeader*)align_buffer(readOnlyBuffer, sizeof(pint_t) == 4 ? 4 : 3);
-
- assert(readOnlyBufferSize > sizeof(ImpCacheHeader));
+ H* cachePtr = (H*)align_buffer(readOnlyBuffer, sizeof(pint_t) == 4 ? 4 : 3);
+
+ assert(readOnlyBufferSize > sizeof(H));
uint64_t occupied = impCache.occupied();
int64_t fallback_class_offset = *(cls->getSuperClassAddress()) - clsVMAddr;
@@ -756,25 +728,27 @@
fallback_class_offset = superclassVMAddr - clsVMAddr;
}
- assert((int32_t)fallback_class_offset == fallback_class_offset);
assert((uint32_t)occupied == occupied);
-
- *cachePtr = (ImpCacheHeader){
- .fallback_class_offset = (int32_t)fallback_class_offset,
- .cache_shift = (uint32_t)(data->shift + 7),
- .cache_mask = (uint32_t)data->mask(),
- .occupied = (uint32_t)occupied,
- .has_inlines = impCache.hasInlines,
- .bit_one = 1, // obj-c plays HORRENDOUS games here
- };
+ if ( cachesFormatVersion < 3 ) {
+ assert((int32_t)fallback_class_offset == fallback_class_offset);
+ cachePtr->fallback_class_offset = (int32_t)fallback_class_offset;
+ } else {
+ assert(sizeof(cachePtr->fallback_class_offset) == sizeof(fallback_class_offset));
+ cachePtr->fallback_class_offset = fallback_class_offset;
+ }
+ cachePtr->cache_shift = (uint32_t)(data->shift + 7);
+ cachePtr->cache_mask = (uint32_t)data->mask();
+ cachePtr->occupied = (uint32_t)occupied;
+ cachePtr->has_inlines = impCache.hasInlines;
+ cachePtr->bit_one = 1; // obj-c plays HORRENDOUS games here
// is this right?
int64_t vmaddr = cache->vmAddrForContent(readOnlyBuffer);
assert((pint_t)vmaddr == (uint64_t)vmaddr);
*vtableAddr = (pint_t)cache->vmAddrForContent(readOnlyBuffer);
d->_aslrTracker->add(vtableAddr);
- readOnlyBuffer += sizeof(ImpCacheHeader);
- readOnlyBufferSize -= sizeof(ImpCacheHeader);
+ readOnlyBuffer += sizeof(H);
+ readOnlyBufferSize -= sizeof(H);
impCache.write(cache, selectorStringVMAddr, clsVMAddr, readOnlyBuffer, readOnlyBufferSize, cachesFormatVersion, diag);
}
@@ -823,10 +797,9 @@
: _protocolCount(0), _protocolReferenceCount(0), _diagnostics(diag), _dylibIndices(dylibIndices) {
}
- void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) {
- const CacheBuilder::CacheCoalescedText::StringSection& classNames = coalescedText.getSectionData("__objc_classname");
- for (const auto& stringAndOffset : classNames.stringsToOffsets) {
- uint64_t vmAddr = classNames.bufferVMAddr + stringAndOffset.second;
+ void visitCoalescedStrings(const CacheBuilder::CoalescedStringsSection& coalescedClassNames) {
+ for (const auto& stringAndOffset : coalescedClassNames.stringsToOffsets) {
+ uint64_t vmAddr = coalescedClassNames.bufferVMAddr + stringAndOffset.second;
_uniquedProtocolNames[stringAndOffset.first.data()] = vmAddr;
}
}
@@ -1059,7 +1032,7 @@
#endif
}
-template <typename P> static inline void emitIMPCaches(ContentAccessor& cacheAccessor,
+template <typename P, typename H> static inline void emitIMPCaches(ContentAccessor& cacheAccessor,
std::vector<SharedCacheBuilder::DylibInfo> & allDylibs,
std::vector<const macho_header<P>*> & sizeSortedDylibs,
std::optional<uint64_t> relativeMethodListBaseAddress,
@@ -1085,8 +1058,8 @@
timeRecorder.recordTime("compute IMP map");
diag.verbose("[IMP caches] emitting IMP caches\n");
- IMPCachesEmitter<P> impCachesEmitter(diag, classRecorder, selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining, allDylibs, sizeSortedDylibs, objcASLRTracker, impCachesVersion);
- ClassWalker<P, IMPCachesEmitter<P>> impEmitterClassWalker(impCachesEmitter, ClassWalkerMode::ClassAndMetaclasses);
+ IMPCachesEmitter<P, H> impCachesEmitter(diag, classRecorder, selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining, allDylibs, sizeSortedDylibs, objcASLRTracker, impCachesVersion);
+ ClassWalker<P, IMPCachesEmitter<P, H>> impEmitterClassWalker(impCachesEmitter, ClassWalkerMode::ClassAndMetaclasses);
for (const macho_header<P>* mh : sizeSortedDylibs) {
impEmitterClassWalker.walk(&cacheAccessor, mh);
if (diag.hasError())
@@ -1099,22 +1072,24 @@
}
template <typename P>
-void doOptimizeObjC(DyldSharedCache* cache, bool forProduction,
- CacheBuilder::LOH_Tracker& lohTracker, const CacheBuilder::CacheCoalescedText& coalescedText,
+void doOptimizeObjC(DyldSharedCache* cache, uint64_t cacheType,
+ CacheBuilder::LOH_Tracker& lohTracker,
+ const CacheBuilder::CoalescedStringsSection& coalescedMethodNames,
+ const CacheBuilder::CoalescedStringsSection& coalescedClassNames,
const std::map<void*, std::string>& missingWeakImports, Diagnostics& diag,
uint8_t* objcReadOnlyBuffer, uint64_t objcReadOnlyBufferSizeUsed, uint64_t objcReadOnlyBufferSizeAllocated,
uint8_t* objcReadWriteBuffer, uint64_t objcReadWriteBufferSizeAllocated,
uint64_t objcRwFileOffset,
std::vector<SharedCacheBuilder::DylibInfo> & allDylibs,
const std::vector<const IMPCaches::Selector*> & inlinedSelectors,
- bool impCachesSuccess,
+ bool impCachesSuccess, int impCachesVersion,
TimeRecorder& timeRecorder)
{
typedef typename P::E E;
typedef typename P::uint_t pint_t;
diag.verbose("Optimizing objc metadata:\n");
- diag.verbose(" cache type is %s\n", forProduction ? "production" : "development");
+ diag.verbose(" cache type is %s\n", DyldSharedCache::getCacheTypeName(cacheType));
ContentAccessor cacheAccessor(cache, diag);
@@ -1151,10 +1126,9 @@
// state which we can access at runtime. We do have the PBLS state today in dyld4, but its not used with
// JIT loaders.
const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
- auto vmAddrConverter = ma->makeVMAddrConverter(false);
Diagnostics diags;
__block bool hasSwiftProtocols = false;
- ma->forEachSwiftProtocolConformance(diags, vmAddrConverter, true,
+ ma->forEachSwiftProtocolConformance(diags,
^(uint64_t protocolConformanceRuntimeOffset,
const dyld3::MachOAnalyzer::SwiftProtocolConformance& protocolConformance,
bool& stopProtocolConformance) {
@@ -1356,7 +1330,7 @@
// Eventually we'll update them to offsets directly to the selector string, from the given base address
SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq);
- selOptimizer.visitCoalescedStrings(coalescedText);
+ selOptimizer.visitCoalescedStrings(coalescedMethodNames);
uint64_t relativeMethodListBaseAddress = 0;
constexpr std::string_view magicSelector = "\xf0\x9f\xa4\xaf";
if ( auto it = selOptimizer.strings().find(magicSelector.data()); it != selOptimizer.strings().end() ) {
@@ -1400,7 +1374,9 @@
missingWeakImports,
sizeSortedDylibs);
- if (forProduction) {
+ bool universalCustomer = (cacheType == kDyldSharedCacheTypeUniversal) && (cache->header.cacheSubType == kDyldSharedCacheTypeProduction);
+
+ if ( cacheType == kDyldSharedCacheTypeProduction || universalCustomer ) {
// Shared cache does not currently support unbound weak references.
// Here we assert that there are none. If support is added later then
// this assertion needs to be removed and this path needs to be tested.
@@ -1419,7 +1395,7 @@
//
// This is SAFE: the binaries themselves are unmodified.
ClassListBuilder<P> classes(dylibIndices);
- classes.visitCoalescedStrings(coalescedText);
+ classes.visitCoalescedStrings(coalescedClassNames);
ClassWalker<P, ClassListBuilder<P>> classWalker(classes);
for (const macho_header<P>* mh : sizeSortedDylibs) {
classWalker.walk(&cacheAccessor, mh);
@@ -1459,7 +1435,7 @@
// This must be done AFTER updating method lists.
ProtocolOptimizer<P> protocolOptimizer(diag, dylibIndices);
- protocolOptimizer.visitCoalescedStrings(coalescedText);
+ protocolOptimizer.visitCoalescedStrings(coalescedClassNames);
for (const macho_header<P>* mh : sizeSortedDylibs) {
protocolOptimizer.addProtocols(&cacheAccessor, mh);
}
@@ -1537,9 +1513,8 @@
//
// Objc has a magic section of imp cache base pointers. We need these to
// offset everything else from
- const CacheBuilder::CacheCoalescedText::StringSection& methodNames = coalescedText.getSectionData("__objc_methname");
- uint64_t selectorStringVMAddr = methodNames.bufferVMAddr;
- uint64_t selectorStringVMSize = methodNames.bufferSize;
+ uint64_t selectorStringVMAddr = coalescedMethodNames.bufferVMAddr;
+ uint64_t selectorStringVMSize = coalescedMethodNames.bufferSize;
uint64_t impCachesVMSize = 0; // We'll calculate this later
uint64_t optRODataRemainingBeforeImpCaches = optRORemaining;
@@ -1549,8 +1524,6 @@
uint8_t* inlinedSelectorsStart = optRWData;
uint8_t* inlinedSelectorsEnd = optRWData;
- int impCachesVersion = 1;
-
uint64_t pointersVMAddr = 0;
if (optImpCachesPointerSection) {
if (optImpCachesPointerSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
@@ -1562,7 +1535,6 @@
bool found = ((dyld3::MachOAnalyzer*)libobjcMH)->findExportedSymbol(diag, "_objc_opt_preopt_caches_version", false, foundInfo, nullptr);
if (found) {
- impCachesVersion = *(int*)((uint8_t*)libobjcMH + foundInfo.value);
found = ((dyld3::MachOAnalyzer*)libobjcMH)->findExportedSymbol(diag, "_objc_opt_offsets", false, foundInfo, nullptr);
if (!found) {
diag.error("libobjc's imp cache pointer list not found (metadata not optimized)");
@@ -1570,15 +1542,20 @@
}
pointersVMAddr = ((dyld3::MachOAnalyzer*)libobjcMH)->preferredLoadAddress() + foundInfo.value;
} else {
- impCachesVersion = 1;
pointersVMAddr = optImpCachesPointerSection->addr();
}
}
if (impCachesSuccess) {
- emitIMPCaches<P>(cacheAccessor, allDylibs, sizeSortedDylibs, relativeMethodListBaseAddress,
- selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining,
- *objcASLRTracker, inlinedSelectors, inlinedSelectorsStart, inlinedSelectorsEnd, impCachesVersion, diag, timeRecorder);
+ if ( impCachesVersion < 3 ) {
+ emitIMPCaches<P, ImpCacheHeader_v1>(cacheAccessor, allDylibs, sizeSortedDylibs, relativeMethodListBaseAddress,
+ selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining,
+ *objcASLRTracker, inlinedSelectors, inlinedSelectorsStart, inlinedSelectorsEnd, impCachesVersion, diag, timeRecorder);
+ } else {
+ emitIMPCaches<P, ImpCacheHeader_v2>(cacheAccessor, allDylibs, sizeSortedDylibs, relativeMethodListBaseAddress,
+ selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining,
+ *objcASLRTracker, inlinedSelectors, inlinedSelectorsStart, inlinedSelectorsEnd, impCachesVersion, diag, timeRecorder);
+ }
}
uint8_t* alignedROData = alignPointer(optROData);
@@ -1607,7 +1584,7 @@
// Collect flags.
uint32_t headerFlags = 0;
- if (forProduction) {
+ if ( cacheType == kDyldSharedCacheTypeProduction || universalCustomer ) {
headerFlags |= objc_opt::IsProduction;
}
if (noMissingWeakSuperclasses) {
@@ -1772,12 +1749,16 @@
} // anon namespace
-size_t IMPCaches::sizeForImpCacheWithCount(int count) {
+size_t IMPCaches::sizeForImpCacheWithCount(int count, int impCachesVersion) {
// The architecture should not be relevant here as it's all offsets and fixed int sizes.
// It was just the most logical place to host this function in.
- size_t size64 = IMPCachesEmitter<Pointer64<LittleEndian>>::sizeForImpCacheWithCount(count);
- size_t size32 = IMPCachesEmitter<Pointer32<LittleEndian>>::sizeForImpCacheWithCount(count);
+ size_t size64 = IMPCachesEmitter<Pointer64<LittleEndian>, ImpCacheHeader_v2>::sizeForImpCacheWithCount(count);
+ size_t size32 = IMPCachesEmitter<Pointer32<LittleEndian>, ImpCacheHeader_v2>::sizeForImpCacheWithCount(count);
+ if (impCachesVersion < 3) {
+ size64 = IMPCachesEmitter<Pointer64<LittleEndian>, ImpCacheHeader_v1>::sizeForImpCacheWithCount(count);
+ size32 = IMPCachesEmitter<Pointer32<LittleEndian>, ImpCacheHeader_v1>::sizeForImpCacheWithCount(count);
+ }
assert(size64 == size32);
return size64;
@@ -1790,26 +1771,32 @@
// Mike suggests all relative method lists are offsets from the magic selector
if ( _archLayout->is64 )
doOptimizeObjC<Pointer64<LittleEndian>>(cache,
- _options.optimizeStubs,
+ _options.cacheConfiguration,
_lohTracker,
- _coalescedText,
+ _objcCoalescedMethodNames,
+ _objcCoalescedClassNames,
_missingWeakImports, _diagnostics,
_objcReadOnlyBuffer,
_objcReadOnlyBufferSizeUsed,
_objcReadOnlyBufferSizeAllocated,
_objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated,
- _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors, impCachesSuccess, _timeRecorder);
+ _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors,
+ impCachesSuccess, _impCachesBuilder->impCachesVersion,
+ _timeRecorder);
else
doOptimizeObjC<Pointer32<LittleEndian>>(cache,
- _options.optimizeStubs,
+ _options.cacheConfiguration,
_lohTracker,
- _coalescedText,
+ _objcCoalescedMethodNames,
+ _objcCoalescedClassNames,
_missingWeakImports, _diagnostics,
_objcReadOnlyBuffer,
_objcReadOnlyBufferSizeUsed,
_objcReadOnlyBufferSizeAllocated,
_objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated,
- _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors, impCachesSuccess, _timeRecorder);
+ _objcReadWriteFileOffset, _sortedDylibs, inlinedSelectors,
+ impCachesSuccess, _impCachesBuilder->impCachesVersion,
+ _timeRecorder);
}
static uint32_t hashTableSize(uint32_t maxElements, uint32_t perElementData)