Loading...
common/ObjCVisitor.cpp dyld-1066.8 dyld-1335
--- dyld/dyld-1066.8/common/ObjCVisitor.cpp
+++ dyld/dyld-1335/common/ObjCVisitor.cpp
@@ -22,6 +22,10 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#include <TargetConditionals.h>
+
+#if !TARGET_OS_EXCLAVEKIT
+
 #include "ObjCVisitor.h"
 
 #if SUPPORT_VM_LAYOUT
@@ -30,10 +34,12 @@
 
 #if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
 #include "ASLRTracker.h"
+#include <unordered_set>
 #endif
 
 using namespace objc_visitor;
-using metadata_visitor::ResolvedValue;
+using ResolvedValue = metadata_visitor::ResolvedValue;
+using mach_o::Header;
 
 #if !SUPPORT_VM_LAYOUT
 using metadata_visitor::Segment;
@@ -142,23 +148,27 @@
 }
 
 #if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
-void Class::setMethodCachePropertiesVMAddr(const Visitor& objcVisitor, VMAddress vmAddr)
+void Class::setMethodCachePropertiesVMAddr(const Visitor& objcVisitor, VMAddress vmAddr,
+                                           const dyld3::MachOFile::PointerMetaData& PMD)
 {
     ResolvedValue field = objcVisitor.getField(this->classPos, this->getFieldPos(objcVisitor, Field::methodCacheProperties));
-    objcVisitor.updateTargetVMAddress(field, CacheVMAddress(vmAddr.rawValue()));
-}
-#endif
-
-#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+    objcVisitor.setTargetVMAddress(field, CacheVMAddress(vmAddr.rawValue()), PMD);
+}
+#endif
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS || BUILDING_SHARED_CACHE_UTIL
 void Class::withSuperclass(const Visitor& objcVisitor,
                            void (^handler)(const dyld3::MachOFile::ChainedFixupPointerOnDisk* fixup, uint16_t pointerFormat)) const
 {
-    assert(objcVisitor.pointerSize == 8);
-
-    assert(objcVisitor.isOnDiskBinary());
+    // HACK: The visitor classes need to be refactored to handle cache util. For now just force the caller of this method in
+    // cache util to have the chain format
+#if BUILDING_SHARED_CACHE_UTIL
+    uint16_t chainedPointerFormat = 0;
+#else
     uint16_t chainedPointerFormat = this->classPos.chainedPointerFormat().value();
-
-    const void* fieldPos = &((const class64_t*)this->classPos.value())->superclassVMAddr;
+#endif
+
+    const void* fieldPos = this->getFieldPos(objcVisitor, Field::superclass);
     dyld3::MachOFile::ChainedFixupPointerOnDisk* fieldFixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)fieldPos;
     handler(fieldFixup, chainedPointerFormat);
 }
@@ -248,6 +258,11 @@
     return ClassData(targetValue);
 }
 
+VMAddress Class::getClassDataVMAddr(const Visitor& objcVisitor) const
+{
+    return getClassData(objcVisitor).getVMAddress();
+}
+
 bool Class::isRootClass(const Visitor& objcVisitor) const
 {
     ClassData data = getClassData(objcVisitor);
@@ -278,6 +293,17 @@
     return objcVisitor.resolveOptionalRebase(field);
 }
 
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+ResolvedValue Class::setBaseMethodsVMAddr(const Visitor& objcVisitor, VMAddress vmAddr,
+                                 const dyld3::MachOFile::PointerMetaData& PMD)
+{
+    ClassData classData = getClassData(objcVisitor);
+    ResolvedValue field = classData.getField(objcVisitor, ClassData::Field::baseMethods);;
+    objcVisitor.setTargetVMAddress(field, CacheVMAddress(vmAddr.rawValue()), PMD);
+    return field;
+}
+#endif
+
 ProtocolList Class::getBaseProtocols(const Visitor& objcVisitor) const
 {
     ClassData classData = getClassData(objcVisitor);
@@ -285,12 +311,39 @@
     return objcVisitor.resolveOptionalRebase(field);
 }
 
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+ResolvedValue Class::setBaseProtocolsVMAddr(const Visitor& objcVisitor, VMAddress vmAddr)
+{
+    ClassData classData = getClassData(objcVisitor);
+    ResolvedValue field = classData.getField(objcVisitor, ClassData::Field::baseProtocols);
+    objcVisitor.updateTargetVMAddress(field, CacheVMAddress(vmAddr.rawValue()));
+    return field;
+}
+#endif
+
 IVarList Class::getIVars(const Visitor& objcVisitor) const
 {
     ClassData classData = getClassData(objcVisitor);
     ResolvedValue field = classData.getField(objcVisitor, ClassData::Field::ivars);
     return objcVisitor.resolveOptionalRebase(field);
 }
+
+PropertyList Class::getBaseProperties(const Visitor& objcVisitor) const
+{
+    ClassData classData = getClassData(objcVisitor);
+    ResolvedValue field = classData.getField(objcVisitor, ClassData::Field::baseProperties);
+    return objcVisitor.resolveOptionalRebase(field);
+}
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+ResolvedValue Class::setBasePropertiesVMAddr(const Visitor& objcVisitor, VMAddress vmAddr)
+{
+    ClassData classData = getClassData(objcVisitor);
+    ResolvedValue field = classData.getField(objcVisitor, ClassData::Field::baseProperties);
+    objcVisitor.updateTargetVMAddress(field, CacheVMAddress(vmAddr.rawValue()));
+    return field;
+}
+#endif
 
 uint32_t Class::getInstanceStart(const Visitor& objcVisitor) const
 {
@@ -419,6 +472,8 @@
                 return &category32->protocolsVMAddr;
             case Field::instanceProperties:
                 return &category32->instancePropertiesVMAddr;
+            case Field::classProperties:
+                return &category32->classPropertiesVMAddr;
         }
     } else {
         const category64_t* category64 = (const category64_t*)this->categoryPos.value();
@@ -435,6 +490,8 @@
                 return &category64->protocolsVMAddr;
             case Field::instanceProperties:
                 return &category64->instancePropertiesVMAddr;
+            case Field::classProperties:
+                return &category64->classPropertiesVMAddr;
         }
     }
 }
@@ -445,6 +502,22 @@
     return (const char*)objcVisitor.resolveRebase(field).value();
 }
 
+VMAddress Category::getVMAddress() const
+{
+    return this->categoryPos.vmAddress();
+}
+
+const void* Category::getLocation() const
+{
+    return this->categoryPos.value();
+}
+
+VMAddress Category::getNameVMAddr(const Visitor& objcVisitor) const
+{
+    ResolvedValue field = objcVisitor.getField(this->categoryPos, this->getFieldPos(objcVisitor, Field::name));
+    return objcVisitor.resolveRebase(field).vmAddress();
+}
+
 MethodList Category::getInstanceMethods(const Visitor& objcVisitor) const
 {
     ResolvedValue field = objcVisitor.getField(this->categoryPos, this->getFieldPos(objcVisitor, Field::instanceMethods));
@@ -463,18 +536,59 @@
     return objcVisitor.resolveOptionalRebase(field);
 }
 
-#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+PropertyList Category::getInstanceProperties(const Visitor& objcVisitor) const
+{
+    ResolvedValue field = objcVisitor.getField(this->categoryPos, this->getFieldPos(objcVisitor, Field::instanceProperties));
+    return objcVisitor.resolveOptionalRebase(field);
+}
+
+PropertyList Category::getClassProperties(const Visitor& objcVisitor) const
+{
+    ResolvedValue field = objcVisitor.getField(this->categoryPos, this->getFieldPos(objcVisitor, Field::classProperties));
+    return objcVisitor.resolveOptionalRebase(field);
+}
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS || BUILDING_SHARED_CACHE_UTIL
 void Category::withClass(const Visitor& objcVisitor,
                          void (^handler)(const dyld3::MachOFile::ChainedFixupPointerOnDisk* fixup, uint16_t pointerFormat)) const
 {
-    assert(objcVisitor.pointerSize == 8);
-
+    // HACK: The visitor classes need to be refactored to handle cache util. For now just force the caller of this method in
+    // cache util to have the chain format
+#if BUILDING_SHARED_CACHE_UTIL
+    uint16_t chainedPointerFormat = 0;
+#else
     assert(objcVisitor.isOnDiskBinary());
     uint16_t chainedPointerFormat = this->categoryPos.chainedPointerFormat().value();
-
-    const void* fieldPos = &((const category64_t*)this->categoryPos.value())->clsVMAddr;
+#endif
+
+    const void* fieldPos = nullptr;
+    if ( objcVisitor.pointerSize == 8 ) {
+        fieldPos = &((const category64_t*)this->categoryPos.value())->clsVMAddr;
+    } else if (objcVisitor.pointerSize == 4 ){
+        fieldPos = &((const category32_t*)this->categoryPos.value())->clsVMAddr;
+    }
+    assert(fieldPos != nullptr);
+
     dyld3::MachOFile::ChainedFixupPointerOnDisk* fieldFixup = (dyld3::MachOFile::ChainedFixupPointerOnDisk*)fieldPos;
     handler(fieldFixup, chainedPointerFormat);
+}
+#endif
+
+uint32_t Category::getSize(bool is64)
+{
+    return is64 ? sizeof(category64_t) : sizeof(category32_t);
+}
+
+
+#if BUILDING_SHARED_CACHE_UTIL
+std::optional<VMAddress> Category::getClassVMAddr(const Visitor& objcVisitor) const
+{
+    ResolvedValue field = objcVisitor.getField(this->categoryPos, this->getFieldPos(objcVisitor, Field::cls));
+    std::optional<ResolvedValue> targetValue = objcVisitor.resolveOptionalRebase(field);
+    if ( targetValue )
+        return targetValue->vmAddress();
+
+    return { };
 }
 #endif
 
@@ -793,6 +907,34 @@
     return methodList->getMethodCount();
 }
 
+uint32_t MethodList::listSize() const
+{
+    if ( !methodListPos.has_value() )
+        return 0;
+
+    const ResolvedValue& methodListValue = this->methodListPos.value();
+
+    const method_list_t* methodList = (const method_list_t*)methodListValue.value();
+    assert(methodList != nullptr);
+
+    uint32_t size = sizeof(uint32_t) * 2;
+    size += methodList->getMethodCount() * methodList->getMethodSize();
+    return size;
+}
+
+uint32_t MethodList::methodSize() const
+{
+    if ( !methodListPos.has_value() )
+        return 0;
+
+    const ResolvedValue& methodListValue = this->methodListPos.value();
+
+    const method_list_t* methodList = (const method_list_t*)methodListValue.value();
+    assert(methodList != nullptr);
+
+    return methodList->getMethodSize();
+}
+
 bool MethodList::usesRelativeOffsets() const
 {
     if ( !methodListPos.has_value() )
@@ -845,6 +987,18 @@
     methodList->setIsSorted();
 }
 
+size_t MethodList::makeEmptyMethodList(void* buffer)
+{
+    assert(buffer != nullptr);
+    method_list_t* methodList = (method_list_t*)buffer;
+    bzero(methodList, sizeof(method_list_t));
+
+    methodList->setIsUniqued();
+    methodList->setIsSorted();
+
+    return sizeof(method_list_t);
+}
+
 void MethodList::setUsesOffsetsFromSelectorBuffer()
 {
     if ( !methodListPos.has_value() )
@@ -858,7 +1012,15 @@
     methodList->setUsesOffsetsFromSelectorBuffer();
 }
 
-#if BUILDING_CACHE_BUILDER_UNIT_TESTS
+bool MethodList::isListOfLists() const
+{
+    if ( !methodListPos.has_value() )
+        return false;
+
+    const ResolvedValue& methodListValue = this->methodListPos.value();
+    return methodListValue.vmAddress().rawValue() & 1;
+}
+
 const void* MethodList::getLocation() const
 {
     if ( !this->methodListPos.has_value() )
@@ -868,9 +1030,10 @@
 
 std::optional<VMAddress> MethodList::getVMAddress() const
 {
+    if ( !this->methodListPos.has_value() )
+        return { };
     return this->methodListPos->vmAddress();
 }
-#endif
 
 static Method::Kind getKind(const MethodList::method_list_t* methodList)
 {
@@ -979,7 +1142,7 @@
             // The uint32_t name field is an offset from itself to a selref.  The selref then points to the selector string
             const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->nameOffset;
             int32_t relativeOffsetFromField = *(int32_t*)fieldPos;
-            VMOffset relativeOffsetFromMethod((uint64_t)__offsetof(relative_method_t, nameOffset) + relativeOffsetFromField);
+            VMOffset relativeOffsetFromMethod((uint64_t)offsetof(relative_method_t, nameOffset) + relativeOffsetFromField);
 
             VMAddress methodVMAddr = this->methodPos.vmAddress();
             VMAddress nameSelRefVMAddr = methodVMAddr + relativeOffsetFromMethod;
@@ -988,7 +1151,17 @@
             return (const char*)objcVisitor.resolveRebase(nameSelRefValue).value();
         }
         case Kind::relativeDirect: {
+#if BUILDING_SHARED_CACHE_UTIL
+            const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->nameOffset;
+            uint32_t nameOffsetInBuffer = *(uint32_t*)fieldPos;
+
+            VMAddress nameVMAddr = objcVisitor.sharedCacheSelectorStringsBaseAddress() + VMOffset((uint64_t)nameOffsetInBuffer);
+            ResolvedValue nameValue = objcVisitor.getValueFor(nameVMAddr);
+            return (const char*)nameValue.value();
+#else
+            // dyld should never walk direct methods as the objc closure optimizations skip cache dylibs
             assert(0);
+#endif
         }
         case Kind::pointer: {
             ResolvedValue nameField = this->getNameField(objcVisitor);
@@ -1003,7 +1176,7 @@
         case Kind::relativeIndirect: {
             const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->typesOffset;
             int32_t relativeOffsetFromField = *(int32_t*)fieldPos;
-            VMOffset relativeOffsetFromMethod((uint64_t)__offsetof(relative_method_t, typesOffset) + relativeOffsetFromField);
+            VMOffset relativeOffsetFromMethod((uint64_t)offsetof(relative_method_t, typesOffset) + relativeOffsetFromField);
 
             VMAddress methodVMAddr = this->methodPos.vmAddress();
             VMAddress typeVMAddr = methodVMAddr + relativeOffsetFromMethod;
@@ -1027,7 +1200,7 @@
         case Kind::relativeIndirect: {
             const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->impOffset;
             int32_t relativeOffsetFromField = *(int32_t*)fieldPos;
-            VMOffset relativeOffsetFromMethod((uint64_t)__offsetof(relative_method_t, impOffset) + relativeOffsetFromField);
+            VMOffset relativeOffsetFromMethod((uint64_t)offsetof(relative_method_t, impOffset) + relativeOffsetFromField);
 
             VMAddress methodVMAddr = this->methodPos.vmAddress();
             VMAddress impVMAddr = methodVMAddr + relativeOffsetFromMethod;
@@ -1054,7 +1227,7 @@
             // The uint32_t name field is an offset from itself to a selref.  The selref then points to the selector string
             const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->nameOffset;
             int32_t relativeOffsetFromField = *(int32_t*)fieldPos;
-            VMOffset relativeOffsetFromMethod((uint64_t)__offsetof(relative_method_t, nameOffset) + relativeOffsetFromField);
+            VMOffset relativeOffsetFromMethod((uint64_t)offsetof(relative_method_t, nameOffset) + relativeOffsetFromField);
 
             VMAddress methodVMAddr = this->methodPos.vmAddress();
             VMAddress nameSelRefVMAddr = methodVMAddr + relativeOffsetFromMethod;
@@ -1063,7 +1236,7 @@
             return objcVisitor.resolveRebase(nameSelRefValue).vmAddress();
         }
         case Kind::relativeDirect: {
-#if BUILDING_DYLD || BUILDING_CLOSURE_UTIL || BUILDING_SHARED_CACHE_UTIL || BUILDING_UNIT_TESTS
+#if BUILDING_DYLD || BUILDING_CLOSURE_UTIL || BUILDING_UNIT_TESTS
             // dyld should never walk direct methods as the objc closure optimizations skip cache dylibs
             assert(0);
 #else
@@ -1087,7 +1260,7 @@
         case Kind::relativeDirect: {
             const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->typesOffset;
             int32_t relativeOffsetFromField = *(int32_t*)fieldPos;
-            VMOffset relativeOffsetFromMethod((uint64_t)__offsetof(relative_method_t, typesOffset) + relativeOffsetFromField);
+            VMOffset relativeOffsetFromMethod((uint64_t)offsetof(relative_method_t, typesOffset) + relativeOffsetFromField);
 
             VMAddress methodVMAddr = this->methodPos.vmAddress();
             VMAddress typeVMAddr = methodVMAddr + relativeOffsetFromMethod;
@@ -1107,7 +1280,12 @@
         case Kind::relativeDirect:  {
             const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->impOffset;
             int32_t relativeOffsetFromField = *(int32_t*)fieldPos;
-            VMOffset relativeOffsetFromMethod((uint64_t)__offsetof(relative_method_t, impOffset) + relativeOffsetFromField);
+
+            // protocols have null impls
+            if ( relativeOffsetFromField == 0 )
+                return std::nullopt;
+
+            VMOffset relativeOffsetFromMethod((uint64_t)offsetof(relative_method_t, impOffset) + relativeOffsetFromField);
 
             VMAddress methodVMAddr = this->methodPos.vmAddress();
             VMAddress impVMAddr = methodVMAddr + relativeOffsetFromMethod;
@@ -1129,7 +1307,7 @@
             // The uint32_t name field is an offset from itself to a selref.  The selref then points to the selector string
             const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->nameOffset;
             int32_t relativeOffsetFromField = *(int32_t*)fieldPos;
-            VMOffset relativeOffsetFromMethod((uint64_t)__offsetof(relative_method_t, nameOffset) + relativeOffsetFromField);
+            VMOffset relativeOffsetFromMethod((uint64_t)offsetof(relative_method_t, nameOffset) + relativeOffsetFromField);
 
             VMAddress methodVMAddr = this->methodPos.vmAddress();
             VMAddress nameSelRefVMAddr = methodVMAddr + relativeOffsetFromMethod;
@@ -1176,7 +1354,7 @@
         case Kind::relativeIndirect:
         case Kind::relativeDirect: {
             VMAddress methodVMAddr = this->methodPos.vmAddress();
-            VMAddress typesFieldVMAddr = methodVMAddr + VMOffset((uint64_t)__offsetof(relative_method_t, typesOffset));
+            VMAddress typesFieldVMAddr = methodVMAddr + VMOffset((uint64_t)offsetof(relative_method_t, typesOffset));
 
             VMOffset typesRelativeOffset = typesVMAddr - typesFieldVMAddr;
             int64_t relativeOffset = (int64_t)typesRelativeOffset.rawValue();
@@ -1198,18 +1376,21 @@
     switch ( this->kind ) {
         case Kind::relativeIndirect:
         case Kind::relativeDirect: {
-            // We don't support NULL imp's with relative method lists.
-            assert(impVMAddr.has_value());
-
-            VMAddress methodVMAddr = this->methodPos.vmAddress();
-            VMAddress impFieldVMAddr = methodVMAddr + VMOffset((uint64_t)__offsetof(relative_method_t, impOffset));
-
-            VMOffset impRelativeOffset = impVMAddr.value() - impFieldVMAddr;
-            int64_t relativeOffset = (int64_t)impRelativeOffset.rawValue();
-
-            const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->impOffset;
-            assert((int32_t)relativeOffset == relativeOffset);
-            *(int32_t*)fieldPos = (int32_t)relativeOffset;
+            if ( !impVMAddr.has_value() ) {
+                // A NULL imp is probably a protocol, and is expected.  Every other IMP in the
+                // protocol is also going to be NULL, so just make sure this one matches
+                assert(!this->getIMPVMAddr(objcVisitor).has_value());
+            } else {
+                VMAddress methodVMAddr = this->methodPos.vmAddress();
+                VMAddress impFieldVMAddr = methodVMAddr + VMOffset((uint64_t)offsetof(relative_method_t, impOffset));
+
+                VMOffset impRelativeOffset = impVMAddr.value() - impFieldVMAddr;
+                int64_t relativeOffset = (int64_t)impRelativeOffset.rawValue();
+
+                const uint8_t* fieldPos = (const uint8_t*)&((const relative_method_t*)this->methodPos.value())->impOffset;
+                assert((int32_t)relativeOffset == relativeOffset);
+                *(int32_t*)fieldPos = (int32_t)relativeOffset;
+            }
             break;
         }
         case Kind::pointer: {
@@ -1248,6 +1429,11 @@
     }
 }
 
+uint32_t Method::getSize(bool is64)
+{
+    return is64 ? sizeof(method64_t) : sizeof(method32_t);
+}
+
 //
 // MARK: --- IVarList methods ---
 //
@@ -1294,6 +1480,61 @@
     return this->ivarListPos->vmAddress();
 }
 #endif
+
+//
+// MARK: --- PropertyList methods ---
+//
+uint32_t PropertyList::numProperties() const
+{
+    if ( !this->propertyListPos.has_value() )
+        return 0;
+
+    const ResolvedValue& propertyListValue = this->propertyListPos.value();
+
+    const property_list_t* propertyList = (const property_list_t*)propertyListValue.value();
+    assert(propertyList != nullptr);
+
+    return propertyList->getCount();
+}
+
+Property PropertyList::getProperty(const Visitor& objcVisitor, uint32_t i) const
+{
+    assert(this->propertyListPos.has_value());
+
+    const ResolvedValue& propertyListValue = this->propertyListPos.value();
+
+    const property_list_t* propertyList = (const property_list_t*)propertyListValue.value();
+    assert(propertyList != nullptr);
+
+    const uint8_t* propertyListBase = propertyList->propertyBase();
+    const uint8_t* property = propertyListBase + (i * propertyList->getElementSize());
+
+    ResolvedValue propertyValue = objcVisitor.getField(propertyListValue, property);
+    return Property(propertyValue);
+}
+
+const void* PropertyList::getLocation() const
+{
+    if ( !this->propertyListPos.has_value() )
+        return nullptr;
+    return this->propertyListPos->value();
+}
+
+std::optional<VMAddress> PropertyList::getVMAddress() const
+{
+    if ( !this->propertyListPos.has_value() )
+        return { };
+    return this->propertyListPos->vmAddress();
+}
+
+bool PropertyList::isListOfLists() const
+{
+    if ( !propertyListPos.has_value() )
+        return false;
+
+    const ResolvedValue& propertyListValue = this->propertyListPos.value();
+    return propertyListValue.vmAddress().rawValue() & 1;
+}
 
 //
 // MARK: --- ProtocolList methods ---
@@ -1349,7 +1590,6 @@
 }
 #endif
 
-#if BUILDING_CACHE_BUILDER_UNIT_TESTS
 const void* ProtocolList::getLocation() const
 {
     if ( !this->protocolListPos.has_value() )
@@ -1359,9 +1599,20 @@
 
 std::optional<VMAddress> ProtocolList::getVMAddress() const
 {
+    if ( !this->protocolListPos.has_value() )
+        return { };
     return this->protocolListPos->vmAddress();
 }
-#endif
+
+bool ProtocolList::isListOfLists() const
+{
+    if ( !protocolListPos.has_value() )
+        return false;
+
+    const ResolvedValue& protocolListValue = this->protocolListPos.value();
+    return protocolListValue.vmAddress().rawValue() & 1;
+}
+
 
 void ProtocolList::dump(const Visitor& objcVisitor) const
 {
@@ -1457,10 +1708,47 @@
 }
 
 //
-// MARK: --- Visitor::DataSection methods ---
-//
-
-std::optional<Visitor::DataSection> Visitor::findObjCDataSection(const char *sectionName) const
+// MARK: --- Property methods ---
+//
+
+const void* Property::getFieldPos(const Visitor& objcVisitor, Field field) const
+{
+    if ( objcVisitor.pointerSize == 4 ) {
+        const property32_t* property32 = (const property32_t*)this->propertyPos.value();
+        switch ( field ) {
+            case Field::name:
+                return &property32->nameVMAddr;
+            case Field::attributes:
+                return &property32->attributesVMAddr;
+        }
+    } else {
+        const property64_t* property64 = (const property64_t*)this->propertyPos.value();
+        switch ( field ) {
+            case Field::name:
+                return &property64->nameVMAddr;
+            case Field::attributes:
+                return &property64->attributesVMAddr;
+        }
+    }
+}
+
+const char* Property::getName(const Visitor& objcVisitor) const
+{
+    ResolvedValue field = objcVisitor.getField(this->propertyPos, this->getFieldPos(objcVisitor, Field::name));
+    return (const char*)objcVisitor.resolveRebase(field).value();
+}
+
+const char* Property::getAttributes(const Visitor& objcVisitor) const
+{
+    ResolvedValue field = objcVisitor.getField(this->propertyPos, this->getFieldPos(objcVisitor, Field::attributes));
+    return (const char*)objcVisitor.resolveRebase(field).value();
+}
+
+//
+// MARK: --- Visitor::Section methods ---
+//
+
+std::optional<Visitor::Section> Visitor::findSection(std::span<const char*> altSegNames, const char *sectionName) const
 {
 #if SUPPORT_VM_LAYOUT
     const dyld3::MachOFile* mf = this->dylibMA;
@@ -1468,34 +1756,51 @@
     const dyld3::MachOFile* mf = this->dylibMF;
 #endif
 
-    __block std::optional<Visitor::DataSection> objcDataSection;
-    mf->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
-        if ( (strcmp(sectInfo.segInfo.segName, "__DATA") != 0) &&
-             (strcmp(sectInfo.segInfo.segName, "__DATA_CONST") != 0) &&
-             (strcmp(sectInfo.segInfo.segName, "__DATA_DIRTY") != 0) )
+    __block std::optional<Visitor::Section> objcDataSection;
+    ((const Header*)mf)->forEachSection(^(const Header::SegmentInfo& segInfo, const Header::SectionInfo& sectInfo, bool& stop) {
+        bool segMatch = std::any_of(altSegNames.begin(), altSegNames.end(), [&sectInfo](const char* segName) {
+            return sectInfo.segmentName == segName;
+        });
+        if ( !segMatch )
             return;
-        if ( strcmp(sectInfo.sectName, sectionName) != 0 )
+        if ( sectInfo.sectionName != sectionName )
             return;
 
 #if SUPPORT_VM_LAYOUT
-        const void* targetValue = (const void*)(sectInfo.sectAddr + this->dylibMA->getSlide());
-        ResolvedValue target(targetValue, VMAddress(sectInfo.sectAddr));
+        const void* targetValue = (const void*)(sectInfo.address + this->dylibMA->getSlide());
+        ResolvedValue target(targetValue, VMAddress(sectInfo.address));
 #else
-        VMOffset offsetInSegment(sectInfo.sectAddr - sectInfo.segInfo.vmAddr);
-        ResolvedValue target(this->segments[sectInfo.segInfo.segIndex], offsetInSegment);
-#endif
-        objcDataSection.emplace(std::move(target), sectInfo.sectSize);
+        VMOffset offsetInSegment(sectInfo.address - segInfo.vmaddr);
+        ResolvedValue target(this->segments[sectInfo.segIndex], offsetInSegment);
+#endif
+        objcDataSection.emplace(std::move(target), sectInfo.size);
 
         stop = true;
     });
     return objcDataSection;
 }
 
+std::optional<Visitor::Section> Visitor::findObjCDataSection(const char *sectionName) const
+{
+    static const char* objcDataSegments[] = {
+        "__DATA", "__DATA_CONST", "__DATA_DIRTY"
+    };
+    return findSection(objcDataSegments, sectionName);
+}
+
+std::optional<Visitor::Section> Visitor::findObjCTextSection(const char *sectionName) const
+{
+    static const char* objcTextSegments[] = {
+        "__TEXT"
+    };
+    return findSection(objcTextSegments, sectionName);
+}
+
 //
 // MARK: --- Visitor methods ---
 //
 
-void Visitor::forEachClass(bool visitMetaClasses, const Visitor::DataSection& classListSection,
+void Visitor::forEachClass(bool visitMetaClasses, const Visitor::Section& classListSection,
                            void (^callback)(Class& objcClass, bool isMetaClass, bool& stopClass))
 {
     assert((classListSection.sectSize % pointerSize) == 0);
@@ -1533,7 +1838,7 @@
 
 void Visitor::forEachClass(bool visitMetaClasses, void (^callback)(Class& objcClass, bool isMetaClass, bool& stopClass))
 {
-    std::optional<DataSection> classListSection = this->findObjCDataSection("__objc_classlist");
+    std::optional<Section> classListSection = this->findObjCDataSection("__objc_classlist");
     if ( !classListSection.has_value() )
         return;
 
@@ -1574,32 +1879,35 @@
 
 void Visitor::forEachCategory(void (^callback)(const Category& objcCategory, bool& stopCategory))
 {
-    std::optional<DataSection> categoryListSection = findObjCDataSection("__objc_catlist");
-    if ( !categoryListSection.has_value() )
-        return;
-
-    assert((categoryListSection->sectSize % pointerSize) == 0);
-    uint64_t numCategories = categoryListSection->sectSize / pointerSize;
-
-    const ResolvedValue& sectionValue = categoryListSection->sectionBase;
-    const uint8_t* sectionBase = (const uint8_t*)sectionValue.value();
-    for ( uint64_t categoryIndex = 0; categoryIndex != numCategories; ++categoryIndex ) {
-        const uint8_t* categoryRefPos = sectionBase + (categoryIndex * pointerSize);
-        ResolvedValue categoryRefValue = this->getField(sectionValue, categoryRefPos);
-
-        // Follow the category reference to get to the actual category
-        ResolvedValue categoryPos = resolveRebase(categoryRefValue);
-        Category objcCategory(categoryPos);
-        bool stopCategory = false;
-        callback(objcCategory, stopCategory);
-        if ( stopCategory )
-            break;
+    for ( bool isCatlist2 : { false, true }) {
+        const char* listSection = isCatlist2 ? "__objc_catlist2" : "__objc_catlist";
+        std::optional<Section> categoryListSection = findObjCDataSection(listSection);
+        if ( !categoryListSection.has_value() )
+            continue;
+
+        assert((categoryListSection->sectSize % pointerSize) == 0);
+        uint64_t numCategories = categoryListSection->sectSize / pointerSize;
+
+        const ResolvedValue& sectionValue = categoryListSection->sectionBase;
+        const uint8_t* sectionBase = (const uint8_t*)sectionValue.value();
+        for ( uint64_t categoryIndex = 0; categoryIndex != numCategories; ++categoryIndex ) {
+            const uint8_t* categoryRefPos = sectionBase + (categoryIndex * pointerSize);
+            ResolvedValue categoryRefValue = this->getField(sectionValue, categoryRefPos);
+
+            // Follow the category reference to get to the actual category
+            ResolvedValue categoryPos = resolveRebase(categoryRefValue);
+            Category objcCategory(categoryPos, isCatlist2);
+            bool stopCategory = false;
+            callback(objcCategory, stopCategory);
+            if ( stopCategory )
+                break;
+        }
     }
 }
 
 void Visitor::forEachProtocol(void (^callback)(const Protocol& objcProtocol, bool& stopProtocol))
 {
-    std::optional<DataSection> protocolListSection = findObjCDataSection("__objc_protolist");
+    std::optional<Section> protocolListSection = findObjCDataSection("__objc_protolist");
     if ( !protocolListSection.has_value() )
         return;
 
@@ -1624,7 +1932,7 @@
 
 void Visitor::forEachSelectorReference(void (^callback)(ResolvedValue& value)) const
 {
-    std::optional<DataSection> selRefsSection = findObjCDataSection("__objc_selrefs");
+    std::optional<Section> selRefsSection = findObjCDataSection("__objc_selrefs");
     if ( !selRefsSection.has_value() )
         return;
 
@@ -1657,7 +1965,7 @@
 
 void Visitor::forEachProtocolReference(void (^callback)(ResolvedValue& value))
 {
-    std::optional<DataSection> protocolRefsSection = findObjCDataSection("__objc_protorefs");
+    std::optional<Section> protocolRefsSection = findObjCDataSection("__objc_protorefs");
     if ( !protocolRefsSection.has_value() )
         return;
 
@@ -1674,3 +1982,126 @@
     }
 }
 
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+void Visitor::forEachMethodList(void (^callback)(MethodList& objcMethodList, std::optional<metadata_visitor::ResolvedValue> extendedMethodTypes))
+{
+    __block std::unordered_set<const void*> visitedLists;
+
+    forEachClassAndMetaClass(^(const objc_visitor::Class& objcClass, bool&) {
+        objc_visitor::MethodList objcMethodList = objcClass.getBaseMethods(*this);
+
+        callback(objcMethodList, std::nullopt);
+        visitedLists.insert(objcMethodList.getLocation());
+    });
+
+    forEachCategory(^(const objc_visitor::Category& objcCategory, bool&) {
+        objc_visitor::MethodList instanceMethodList = objcCategory.getInstanceMethods(*this);
+        objc_visitor::MethodList classMethodList    = objcCategory.getClassMethods(*this);
+
+        callback(instanceMethodList, std::nullopt);
+        visitedLists.insert(instanceMethodList.getLocation());
+
+        callback(classMethodList, std::nullopt);
+        visitedLists.insert(classMethodList.getLocation());
+    });
+
+    forEachProtocol(^(const objc_visitor::Protocol& objcProtocol, bool&) {
+        objc_visitor::MethodList instanceMethodList         = objcProtocol.getInstanceMethods(*this);
+        objc_visitor::MethodList classMethodList            = objcProtocol.getClassMethods(*this);
+        objc_visitor::MethodList optionalInstanceMethodList = objcProtocol.getOptionalInstanceMethods(*this);
+        objc_visitor::MethodList optionalClassMethodList    = objcProtocol.getOptionalClassMethods(*this);
+
+        // This is an optional flat array with entries for all method lists.
+        // Each method list of length N has N char* entries in this list, if its present
+        std::optional<metadata_visitor::ResolvedValue> extendedMethodTypes = objcProtocol.getExtendedMethodTypes(*this);
+        const uint8_t* currentMethodTypes = extendedMethodTypes.has_value() ? (const uint8_t*)extendedMethodTypes->value() : nullptr;
+
+        callback(instanceMethodList, extendedMethodTypes);
+        visitedLists.insert(instanceMethodList.getLocation());
+        if ( extendedMethodTypes.has_value() ) {
+            currentMethodTypes += (instanceMethodList.numMethods() * pointerSize);
+            extendedMethodTypes.emplace(metadata_visitor::ResolvedValue(extendedMethodTypes.value(), currentMethodTypes));
+        }
+
+        callback(classMethodList, extendedMethodTypes);
+        visitedLists.insert(classMethodList.getLocation());
+        if ( extendedMethodTypes.has_value() ) {
+            currentMethodTypes += (classMethodList.numMethods() * pointerSize);
+            extendedMethodTypes.emplace(metadata_visitor::ResolvedValue(extendedMethodTypes.value(), currentMethodTypes));
+        }
+
+        callback(optionalInstanceMethodList, extendedMethodTypes);
+        visitedLists.insert(optionalInstanceMethodList.getLocation());
+        if ( extendedMethodTypes.has_value() ) {
+            currentMethodTypes += (optionalInstanceMethodList.numMethods() * pointerSize);
+            extendedMethodTypes.emplace(metadata_visitor::ResolvedValue(extendedMethodTypes.value(), currentMethodTypes));
+        }
+
+        callback(optionalClassMethodList, extendedMethodTypes);
+        visitedLists.insert(optionalClassMethodList.getLocation());
+        if ( extendedMethodTypes.has_value() ) {
+            currentMethodTypes += (optionalClassMethodList.numMethods() * pointerSize);
+            extendedMethodTypes.emplace(metadata_visitor::ResolvedValue(extendedMethodTypes.value(), currentMethodTypes));
+        }
+    });
+
+    // rdar://129304028 (dyld cache builder support for relative method lists in Swift generic classes)
+    // Also scan the entire __objc_methlist section looking for other method lists that
+    // aren't referenced through the regular ObjC metadata.
+    std::optional<Section> methodListSection = findObjCTextSection("__objc_methlist");
+    if ( !methodListSection.has_value() )
+        return;
+    assert((methodListSection->sectSize % 4) == 0);
+
+    const ResolvedValue& sectionValue = methodListSection->sectionBase;
+    const uint8_t* sectionPos = (const uint8_t*)sectionValue.value();
+    const uint8_t* sectionEnd = (const uint8_t*)sectionValue.value() + methodListSection->sectSize;
+
+    while ( sectionPos < sectionEnd ) {
+        ResolvedValue methodListValue = this->getField(sectionValue, sectionPos);
+
+        // method lists are 8-byte alligned, a valid method list can never start
+        // with a 0 because that's where the method size entry and flags are encoded
+        if ( *(uint32_t*)methodListValue.value() == 0 ) {
+            sectionPos += sizeof(uint32_t);
+            continue;
+        }
+
+        MethodList methodList(methodListValue);
+
+        // sanity check entry - all lists in __objc_methlist are relative and
+        // a relative method list entry is 12 bytes large
+        assert(methodList.usesRelativeOffsets() && methodList.methodSize() == 12
+                && "not a relative method list");
+
+        // skip method lists that were visited through classes etc.
+        if ( !visitedLists.contains(methodList.getLocation()) ) {
+            callback(methodList, std::nullopt);
+        }
+
+        uint32_t size = methodList.listSize();
+        assert(size != 0 && "method list can't be empty");
+        sectionPos += size;
+    }
+    assert(sectionPos == sectionEnd && "malformed __objc_methlist section");
+}
+#endif
+
+void Visitor::withImageInfo(void (^callback)(const uint32_t version, const uint32_t flags)) const
+{
+    std::optional<Section> imageInfoSection = findObjCDataSection("__objc_imageinfo");
+    if ( !imageInfoSection.has_value() )
+        return;
+
+    assert((imageInfoSection->sectSize % pointerSize) == 0);
+    const ResolvedValue& sectionValue = imageInfoSection->sectionBase;
+
+    struct objc_image_info {
+        int32_t version;
+        uint32_t flags;
+    };
+    const objc_image_info* sectionBase = (const objc_image_info*)sectionValue.value();
+    callback(sectionBase->version, sectionBase->flags);
+}
+
+#endif // !TARGET_OS_EXCLAVEKIT