Loading...
common/MachOFile.cpp dyld-1122.1 dyld-1335
--- dyld/dyld-1122.1/common/MachOFile.cpp
+++ dyld/dyld-1335/common/MachOFile.cpp
@@ -52,18 +52,41 @@
 }
 #endif
 
+
+
+
 #include "Defines.h"
 
 #include <mach-o/nlist.h>
 
+#if !BUILDING_DYLD
+  #include <vector>
+#endif // !BUILDING_DYLD
+
+#include "Architecture.h"
 #include "Array.h"
+#include "Header.h"
 #include "MachOFile.h"
+#include "Platform.h"
 #include "SupportedArchs.h"
+#include "Universal.h"
 #include "CodeSigningTypes.h"
+
+#include "ObjC.h"
 
 #if (BUILDING_DYLD || BUILDING_LIBDYLD) && !TARGET_OS_EXCLAVEKIT
     #include <subsystem.h>
 #endif
+
+#if !BUILDING_DYLD
+#include "ObjCVisitor.h"
+#endif
+
+using mach_o::GradedArchitectures;
+using mach_o::Header;
+using mach_o::Platform;
+using mach_o::Universal;
+using mach_o::Version32;
 
 namespace dyld3 {
 
@@ -245,268 +268,13 @@
     this->forEachSlice(diag, fileLen, false, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
         if ( needComma )
             strlcat(strBuf, ",", 256);
-        strlcat(strBuf, MachOFile::archName(sliceCpuType, sliceCpuSubType), 256);
+        strlcat(strBuf, mach_o::Architecture(sliceCpuType, sliceCpuSubType).name(), 256);
         needComma = true;
     });
     return strBuf;
 }
 
-bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, bool isOSBinary,
-                                 uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
-{
-    missingSlice = false;
-    if ( (this->magic != OSSwapBigToHostInt32(FAT_MAGIC)) && (this->magic != OSSwapBigToHostInt32(FAT_MAGIC_64)) )
-        return false;
-
-    __block int bestGrade = 0;
-    forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
-        if (int sliceGrade = archs.grade(sliceCpuType, sliceCpuSubType, isOSBinary)) {
-            if ( sliceGrade > bestGrade ) {
-                sliceOffset = (char*)sliceStart - (char*)this;
-                sliceLen    = sliceSize;
-                bestGrade   = sliceGrade;
-            }
-        }
-    });
-    if ( diag.hasError() )
-        return false;
-
-    if ( bestGrade == 0 )
-        missingSlice = true;
-
-    return (bestGrade != 0);
-}
-
-
-////////////////////////////  GradedArchs ////////////////////////////////////////
-
-
-#define GRADE_i386        CPU_TYPE_I386,       CPU_SUBTYPE_I386_ALL,    false
-#define GRADE_x86_64      CPU_TYPE_X86_64,     CPU_SUBTYPE_X86_64_ALL,  false
-#define GRADE_x86_64h     CPU_TYPE_X86_64,     CPU_SUBTYPE_X86_64_H,    false
-#define GRADE_armv7       CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7,      false
-#define GRADE_armv7s      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7S,     false
-#define GRADE_armv7k      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7K,     false
-#define GRADE_armv6m      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V6M,     false
-#define GRADE_armv7m      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7M,     false
-#define GRADE_armv7em     CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7EM,    false
-#define GRADE_armv8m      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V8M,     false
-#define GRADE_arm64       CPU_TYPE_ARM64,      CPU_SUBTYPE_ARM64_ALL,   false
-#define GRADE_arm64e      CPU_TYPE_ARM64,      CPU_SUBTYPE_ARM64E,      false
-#define GRADE_arm64e_pb   CPU_TYPE_ARM64,      CPU_SUBTYPE_ARM64E,      true
-#define GRADE_arm64_32    CPU_TYPE_ARM64_32,   CPU_SUBTYPE_ARM64_32_V8, false
-
-const GradedArchs GradedArchs::i386              = GradedArchs({GRADE_i386,    1});
-const GradedArchs GradedArchs::x86_64            = GradedArchs({GRADE_x86_64,  1});
-const GradedArchs GradedArchs::x86_64h           = GradedArchs({GRADE_x86_64h, 2}, {GRADE_x86_64, 1});
-const GradedArchs GradedArchs::arm64             = GradedArchs({GRADE_arm64,   1});
-#if SUPPORT_ARCH_arm64e
-const GradedArchs GradedArchs::arm64e_keysoff    = GradedArchs({GRADE_arm64e,    2}, {GRADE_arm64, 1});
-const GradedArchs GradedArchs::arm64e_keysoff_pb = GradedArchs({GRADE_arm64e_pb, 2}, {GRADE_arm64, 1});
-const GradedArchs GradedArchs::arm64e            = GradedArchs({GRADE_arm64e,    1});
-const GradedArchs GradedArchs::arm64e_pb         = GradedArchs({GRADE_arm64e_pb, 1});
-#endif
-const GradedArchs GradedArchs::armv7             = GradedArchs({GRADE_armv7,   1});
-const GradedArchs GradedArchs::armv7s            = GradedArchs({GRADE_armv7s,  2}, {GRADE_armv7, 1});
-const GradedArchs GradedArchs::armv7k            = GradedArchs({GRADE_armv7k,  1});
-const GradedArchs GradedArchs::armv7m            = GradedArchs({GRADE_armv7m,  1});
-const GradedArchs GradedArchs::armv7em           = GradedArchs({GRADE_armv7em,  1});
-
-
-#if SUPPORT_ARCH_arm64_32
-const GradedArchs GradedArchs::arm64_32          = GradedArchs({GRADE_arm64_32, 1});
-#endif
-#if BUILDING_LIBDYLD || BUILDING_UNIT_TESTS
-const GradedArchs GradedArchs::launch_AS         = GradedArchs({GRADE_arm64e,  3}, {GRADE_arm64,  2}, {GRADE_x86_64, 1});
-const GradedArchs GradedArchs::launch_AS_Sim     = GradedArchs({GRADE_arm64,   2}, {GRADE_x86_64, 1});
-const GradedArchs GradedArchs::launch_Intel_h    = GradedArchs({GRADE_x86_64h, 3}, {GRADE_x86_64, 2}, {GRADE_i386, 1});
-const GradedArchs GradedArchs::launch_Intel      = GradedArchs({GRADE_x86_64,  2}, {GRADE_i386,   1});
-const GradedArchs GradedArchs::launch_Intel_Sim  = GradedArchs({GRADE_x86_64,  2}, {GRADE_i386,   1});
-#endif
-
-int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype, bool isOSBinary) const
-{
-    for (const auto& p : _orderedCpuTypes) {
-        if (p.type == 0) { break; }
-        if ( (p.type == cputype) && (p.subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) ) {
-            if ( p.osBinary ) {
-                if ( isOSBinary )
-                    return p.grade;
-            }
-            else {
-                return p.grade;
-            }
-        }
-    }
-    return 0;
-}
-
-const char* GradedArchs::name() const
-{
-    return MachOFile::archName(_orderedCpuTypes[0].type, _orderedCpuTypes[0].subtype);
-}
-
-void GradedArchs::forEachArch(bool platformBinariesOnly, void (^handler)(const char*)) const
-{
-    for (const auto& p : _orderedCpuTypes) {
-        if (p.type == 0)
-            break;
-        if ( p.osBinary && !platformBinariesOnly )
-            continue;
-        handler(MachOFile::archName(p.type, p.subtype));
-    }
-}
-
-bool GradedArchs::checksOSBinary() const
-{
-    for (const auto& p : _orderedCpuTypes) {
-        if (p.type == 0) { return false; }
-        if ( p.osBinary ) { return true; }
-    }
-    __builtin_unreachable();
-}
-
-bool GradedArchs::supports64() const
-{
-    return (_orderedCpuTypes.front().type & CPU_ARCH_ABI64) != 0;
-}
-
-#if __x86_64__
-static bool isHaswell()
-{
-    // FIXME: figure out a commpage way to check this
-    struct host_basic_info info;
-    mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
-    mach_port_t hostPort = mach_host_self();
-    kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
-    mach_port_deallocate(mach_task_self(), hostPort);
-    return (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
-}
-#endif
-
-const GradedArchs& GradedArchs::forCurrentOS(bool keysOff, bool osBinariesOnly)
-{
-#if __arm64e__
-    if ( osBinariesOnly )
-        return (keysOff ? arm64e_keysoff_pb : arm64e_pb);
-    else
-        return (keysOff ? arm64e_keysoff : arm64e);
-#elif __ARM64_ARCH_8_32__
-    return arm64_32;
-#elif __arm64__
-    return arm64;
-#elif __ARM_ARCH_7K__
-    return armv7k;
-#elif __ARM_ARCH_7S__
-    return armv7s;
-#elif __ARM_ARCH_7A__
-    return armv7;
-#elif __x86_64__
- #if TARGET_OS_SIMULATOR
-    return x86_64;
-  #else
-    return isHaswell() ? x86_64h : x86_64;
-  #endif
-#elif __i386__
-    return i386;
-#else
-    #error unknown platform
-#endif
-}
-
-#if BUILDING_LIBDYLD || BUILDING_UNIT_TESTS
-const GradedArchs& GradedArchs::launchCurrentOS(const char* simArches)
-{
-#if TARGET_OS_SIMULATOR
-    // on Apple Silicon, there is both an arm64 and an x86_64 (under rosetta) simulators
-    // You cannot tell if you are running under rosetta, so CoreSimulator sets SIMULATOR_ARCHS
-    if ( strcmp(simArches, "arm64 x86_64") == 0 )
-        return launch_AS_Sim;
-    else
-        return x86_64;
-#elif TARGET_OS_OSX
-  #if __arm64__
-    return launch_AS;
-  #else
-    return isHaswell() ? launch_Intel_h : launch_Intel;
-  #endif
-#else
-    // all other platforms use same grading for executables as dylibs
-    return forCurrentOS(true, false);
-#endif
-}
-#endif // BUILDING_LIBDYLD
-
-const GradedArchs& GradedArchs::forName(const char* archName, bool keysOff)
-{
-    if (strcmp(archName, "x86_64h") == 0 )
-        return x86_64h;
-    else if (strcmp(archName, "x86_64") == 0 )
-        return x86_64;
-#if SUPPORT_ARCH_arm64e
-    else if (strcmp(archName, "arm64e") == 0 )
-        return keysOff ? arm64e_keysoff : arm64e;
-#endif
-    else if (strcmp(archName, "arm64") == 0 )
-        return arm64;
-    else if (strcmp(archName, "armv7k") == 0 )
-        return armv7k;
-    else if (strcmp(archName, "armv7s") == 0 )
-        return armv7s;
-    else if (strcmp(archName, "armv7") == 0 )
-        return armv7;
-    else if (strcmp(archName, "armv7m") == 0 )
-        return armv7m;
-    else if (strcmp(archName, "armv7em") == 0 )
-        return armv7em;
-#if SUPPORT_ARCH_arm64_32
-    else if (strcmp(archName, "arm64_32") == 0 )
-        return arm64_32;
-#endif
-    else if (strcmp(archName, "i386") == 0 )
-        return i386;
-    assert(0 && "unknown arch name");
-}
-
-
-
 ////////////////////////////  MachOFile ////////////////////////////////////////
-
-
-const MachOFile::ArchInfo MachOFile::_s_archInfos[] = {
-    { "x86_64",   CPU_TYPE_X86_64,   CPU_SUBTYPE_X86_64_ALL  },
-    { "x86_64h",  CPU_TYPE_X86_64,   CPU_SUBTYPE_X86_64_H    },
-    { "i386",     CPU_TYPE_I386,     CPU_SUBTYPE_I386_ALL    },
-    { "arm64",    CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64_ALL   },
-#if SUPPORT_ARCH_arm64e
-    { "arm64e",   CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64E     },
-#endif
-#if SUPPORT_ARCH_arm64_32
-    { "arm64_32", CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8 },
-#endif
-    { "armv7k",   CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7K     },
-    { "armv7s",   CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7S     },
-    { "armv7",    CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7      },
-    { "armv6m",   CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V6M     },
-    { "armv7m",   CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7M     },
-    { "armv7em",  CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7EM    },
-    { "armv8m",   CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V8M     },
-};
-
-const MachOFile::PlatformInfo MachOFile::_s_platformInfos[] = {
-    { "macOS",       Platform::macOS,             LC_VERSION_MIN_MACOSX   },
-    { "iOS",         Platform::iOS,               LC_VERSION_MIN_IPHONEOS },
-    { "tvOS",        Platform::tvOS,              LC_VERSION_MIN_TVOS     },
-    { "watchOS",     Platform::watchOS,           LC_VERSION_MIN_WATCHOS  },
-    { "bridgeOS",    Platform::bridgeOS,          LC_BUILD_VERSION        },
-    { "MacCatalyst", Platform::iOSMac,            LC_BUILD_VERSION        },
-    { "iOS-sim",     Platform::iOS_simulator,     LC_BUILD_VERSION        },
-    { "tvOS-sim",    Platform::tvOS_simulator,    LC_BUILD_VERSION        },
-    { "watchOS-sim", Platform::watchOS_simulator, LC_BUILD_VERSION        },
-    { "driverKit",   Platform::driverKit,         LC_BUILD_VERSION        },
-};
-
-
 
 bool MachOFile::is64() const
 {
@@ -550,280 +318,16 @@
 
 bool MachOFile::isArch(const char* aName) const
 {
-    return (strcmp(aName, archName(this->cputype, this->cpusubtype)) == 0);
-}
-
-const char* MachOFile::archName(uint32_t cputype, uint32_t cpusubtype)
-{
-    for (const ArchInfo& info : _s_archInfos) {
-        if ( (cputype == info.cputype) && ((cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) ) {
-            return info.name;
-        }
-    }
-    return "unknown";
-}
-
-bool MachOFile::cpuTypeFromArchName(const char* archName, cpu_type_t* cputype, cpu_subtype_t* cpusubtype)
-{
-   for (const ArchInfo& info : _s_archInfos) {
-        if ( strcmp(archName, info.name) == 0 ) {
-            *cputype    = info.cputype;
-            *cpusubtype = info.cpusubtype;
-            return true;
-       }
-    }
-    return false;
+    return (strcmp(aName, mach_o::Architecture(this->cputype, this->cpusubtype).name()) == 0);
 }
 
 const char* MachOFile::archName() const
 {
-    return archName(this->cputype, this->cpusubtype);
-}
-
-static void appendDigit(char*& s, unsigned& num, unsigned place, bool& startedPrinting)
-{
-    if ( num >= place ) {
-        unsigned dig = (num/place);
-        *s++ = '0' + dig;
-        num -= (dig*place);
-        startedPrinting = true;
-    }
-    else if ( startedPrinting ) {
-        *s++ = '0';
-    }
-}
-
-static void appendNumber(char*& s, unsigned num)
-{
-    assert(num < 99999);
-    bool startedPrinting = false;
-    appendDigit(s, num, 10000, startedPrinting);
-    appendDigit(s, num,  1000, startedPrinting);
-    appendDigit(s, num,   100, startedPrinting);
-    appendDigit(s, num,    10, startedPrinting);
-    appendDigit(s, num,     1, startedPrinting);
-    if ( !startedPrinting )
-        *s++ = '0';
-}
-
-void MachOFile::packedVersionToString(uint32_t packedVersion, char versionString[32])
-{
-    // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
-    char* s = versionString;
-    appendNumber(s, (packedVersion >> 16));
-    *s++ = '.';
-    appendNumber(s, (packedVersion >> 8) & 0xFF);
-    if ( (packedVersion & 0xFF) != 0 ) {
-        *s++ = '.';
-        appendNumber(s, (packedVersion & 0xFF));
-    }
-    *s++ = '\0';
-}
-
-bool MachOFile::builtForPlatform(Platform reqPlatform, bool onlyOnePlatform) const
-{
-    __block bool foundRequestedPlatform = false;
-    __block bool foundOtherPlatform     = false;
-    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
-        if ( platform == reqPlatform )
-            foundRequestedPlatform = true;
-        else
-            foundOtherPlatform = true;
-    });
-    // if checking that this binary is built for exactly one platform, fail if more
-    if ( foundOtherPlatform && onlyOnePlatform )
-        return false;
-    if ( foundRequestedPlatform )
-        return true;
-
-    // binary has no explict load command to mark platform
-    // could be an old macOS binary, look at arch
-    if  ( !foundOtherPlatform && (reqPlatform == Platform::macOS) ) {
-        if ( this->cputype == CPU_TYPE_X86_64 )
-            return true;
-        if ( this->cputype == CPU_TYPE_I386 )
-            return true;
-    }
-
-#if BUILDING_DYLDINFO
-    // Allow offline tools to analyze binaries dyld doesn't load, ie, those with platforms
-    if ( !foundOtherPlatform && (reqPlatform == Platform::unknown) )
-        return true;
-#endif
-
-    return false;
-}
-
-bool MachOFile::loadableIntoProcess(Platform processPlatform, const char* path, bool internalInstall) const
-{
-    if ( this->builtForPlatform(processPlatform) )
-        return true;
-
-    // Some host macOS dylibs can be loaded into simulator processes
-    if ( MachOFile::isSimulatorPlatform(processPlatform) && this->builtForPlatform(Platform::macOS)) {
-        static const char* const macOSHost[] = {
-            "/usr/lib/system/libsystem_kernel.dylib",
-            "/usr/lib/system/libsystem_platform.dylib",
-            "/usr/lib/system/libsystem_pthread.dylib",
-            "/usr/lib/system/libsystem_platform_debug.dylib",
-            "/usr/lib/system/libsystem_pthread_debug.dylib",
-            "/usr/lib/system/host/liblaunch_sim.dylib",
-        };
-        for (const char* libPath : macOSHost) {
-            if (strcmp(libPath, path) == 0)
-                return true;
-        }
-    }
-
-    // If this is being called on main executable where we expect a macOS program, Catalyst programs are also runnable
-    if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOSMac, true) )
-        return true;
-#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
-    if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOS, true) )
-        return true;
-#endif
-
-
-    bool iOSonMac = (processPlatform == Platform::iOSMac);
-#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
-    // allow iOS binaries in iOSApp
-    if ( processPlatform == Platform::iOS ) {
-        // can load Catalyst binaries into iOS process
-        if ( this->builtForPlatform(Platform::iOSMac) )
-            return true;
-        iOSonMac = true;
-    }
-#endif
-    // macOS dylibs can be loaded into iOSMac processes
-    if ( (iOSonMac) && this->builtForPlatform(Platform::macOS, true) )
-        return true;
-
-
-    return false;
-}
-
-bool MachOFile::isZippered() const
-{
-    __block bool macOS = false;
-    __block bool iOSMac = false;
-    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
-        if ( platform == Platform::macOS )
-            macOS = true;
-        else if ( platform == Platform::iOSMac )
-            iOSMac = true;
-    });
-    return macOS && iOSMac;
+    return mach_o::Architecture(this->cputype, this->cpusubtype).name();
 }
 
 bool MachOFile::inDyldCache() const {
     return (this->flags & MH_DYLIB_IN_CACHE);
-}
-
-Platform MachOFile::currentPlatform()
-{
-#if TARGET_OS_SIMULATOR
-  #if TARGET_OS_WATCH
-    return Platform::watchOS_simulator;
-  #elif TARGET_OS_TV
-    return Platform::tvOS_simulator;
-  #else
-    return Platform::iOS_simulator;
-  #endif
-#elif TARGET_OS_BRIDGE
-    return Platform::bridgeOS;
-#elif TARGET_OS_WATCH
-    return Platform::watchOS;
-#elif TARGET_OS_TV
-    return Platform::tvOS;
-#elif TARGET_OS_IOS
-    return Platform::iOS;
-#elif TARGET_OS_OSX
-    return Platform::macOS;
-#elif TARGET_OS_DRIVERKIT
-    return Platform::driverKit;
-#else
-    #error unknown platform
-#endif
-}
-
-Platform MachOFile::basePlatform(dyld3::Platform reqPlatform) {
-    switch(reqPlatform) {
-        case Platform::unknown:               return Platform::unknown;
-        case Platform::macOS:                 return Platform::macOS;
-        case Platform::iOS:                   return Platform::iOS;
-        case Platform::tvOS:                  return Platform::tvOS;
-        case Platform::watchOS:               return Platform::watchOS;
-        case Platform::bridgeOS:              return Platform::bridgeOS;
-        case Platform::iOSMac:                return Platform::iOS;
-        case Platform::iOS_simulator:         return Platform::iOS;
-        case Platform::tvOS_simulator:        return Platform::tvOS;
-        case Platform::watchOS_simulator:     return Platform::watchOS;
-        case Platform::driverKit:             return Platform::driverKit;
-        default:                              return Platform::unknown;
-    }
-}
-
-
-const char* MachOFile::currentArchName()
-{
-#if __ARM_ARCH_7K__
-    return "armv7k";
-#elif __ARM_ARCH_7A__
-    return "armv7";
-#elif __ARM_ARCH_7S__
-    return "armv7s";
-#elif __arm64e__
-    return "arm64e";
-#elif __arm64__
-#if __LP64__
-    return "arm64";
-#else
-    return "arm64_32";
-#endif
-#elif __x86_64__
-    return isHaswell() ? "x86_64h" : "x86_64";
-#elif __i386__
-    return "i386";
-#else
-    #error unknown arch
-#endif
-}
-
-bool MachOFile::isSimulatorPlatform(Platform platform, Platform* basePlatform)
-{
-    switch ( platform ) {
-        case Platform::iOS_simulator:
-            if ( basePlatform )
-                *basePlatform = Platform::iOS;
-            return true;
-        case Platform::watchOS_simulator:
-            if ( basePlatform )
-                *basePlatform = Platform::watchOS;
-            return true;
-        case Platform::tvOS_simulator:
-            if ( basePlatform )
-                *basePlatform = Platform::tvOS;
-            return true;
-       default:
-            return false;
-    }
-}
-
-bool MachOFile::isBuiltForSimulator() const
-{
-    __block bool result = false;
-    this->forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
-        switch ( platform ) {
-            case Platform::iOS_simulator:
-            case Platform::watchOS_simulator:
-            case Platform::tvOS_simulator:
-                result = true;
-                break;
-           default:
-                break;
-        }
-    });
-    return result;
 }
 
 bool MachOFile::isDyld() const
@@ -895,95 +399,6 @@
 {
     return (this->filetype == MH_PRELOAD);
 }
-
-const char* MachOFile::platformName(Platform reqPlatform)
-{
-    for (const PlatformInfo& info : _s_platformInfos) {
-        if ( info.platform == reqPlatform )
-            return info.name;
-    }
-    return "unknown";
-}
-
-void MachOFile::forEachSupportedPlatform(void (^handler)(Platform platform, uint32_t minOS, uint32_t sdk)) const
-{
-    Diagnostics diag;
-    __block bool foundPlatform = false;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        const build_version_command* buildCmd = (build_version_command *)cmd;
-        const version_min_command*   versCmd  = (version_min_command*)cmd;
-        uint32_t                     sdk;
-        switch ( cmd->cmd ) {
-            case LC_BUILD_VERSION:
-                handler((Platform)(buildCmd->platform), buildCmd->minos, buildCmd->sdk);
-                foundPlatform = true;
-                break;
-            case LC_VERSION_MIN_MACOSX:
-                sdk = versCmd->sdk;
-                // The original LC_VERSION_MIN_MACOSX did not have an sdk field, assume sdk is same as minOS for those old binaries
-                if ( sdk == 0 )
-                    sdk = versCmd->version;
-                handler(Platform::macOS, versCmd->version, sdk);
-                foundPlatform = true;
-                break;
-            case LC_VERSION_MIN_IPHONEOS:
-                if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
-                    handler(Platform::iOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
-                else
-                    handler(Platform::iOS, versCmd->version, versCmd->sdk);
-                foundPlatform = true;
-                break;
-            case LC_VERSION_MIN_TVOS:
-                if ( this->cputype == CPU_TYPE_X86_64 )
-                    handler(Platform::tvOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
-                else
-                    handler(Platform::tvOS, versCmd->version, versCmd->sdk);
-                foundPlatform = true;
-                break;
-            case LC_VERSION_MIN_WATCHOS:
-                if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
-                    handler(Platform::watchOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
-                else
-                    handler(Platform::watchOS, versCmd->version, versCmd->sdk);
-                foundPlatform = true;
-                break;
-        }
-    });
-    if ( !foundPlatform ) {
-        // old binary with no explicit platform
-#if (BUILDING_DYLD || BUILDING_CLOSURE_UTIL) && TARGET_OS_OSX
-        if ( this->cputype == CPU_TYPE_X86_64 )
-            handler(Platform::macOS, 0x000A0500, 0x000A0500); // guess it is a macOS 10.5 binary
-        // <rdar://problem/75343399>
-        // The Go linker emits non-standard binaries without a platform and we have to live with it.
-        if ( this->cputype == CPU_TYPE_ARM64 )
-            handler(Platform::macOS, 0x000B0000, 0x000B0000); // guess it is a macOS 11.0 binary
-#endif
-    }
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-}
-
-void MachOFile::forEachSupportedBuildTool(void (^handler)(Platform platform, uint32_t tool, uint32_t version)) const
-{
-    Diagnostics diag;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        switch ( cmd->cmd ) {
-            case LC_BUILD_VERSION: {
-                const build_version_command* buildCmd = (build_version_command *)cmd;
-                for ( uint32_t i = 0; i != buildCmd->ntools; ++i ) {
-                    uint32_t offset = sizeof(build_version_command) + (i * sizeof(build_tool_version));
-                    if ( offset >= cmd->cmdsize )
-                        break;
-
-                    const build_tool_version* firstTool = (const build_tool_version*)(&buildCmd[1]);
-                    handler((Platform)(buildCmd->platform), firstTool[i].tool, firstTool[i].version);
-                }
-            }
-        }
-    });
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-}
-
 
 bool MachOFile::isMachO(Diagnostics& diag, uint64_t fileSize) const
 {
@@ -1125,12 +540,12 @@
 bool MachOFile::hasObjC() const
 {
     __block bool result = false;
-    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
-        if ( (strcmp(info.sectName, "__objc_imageinfo") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
+    forEachSection(^(const Header::SectionInfo& info, bool& stop) {
+        if ( (info.sectionName == "__objc_imageinfo") && info.segmentName.starts_with("__DATA") ) {
             result = true;
             stop = true;
         }
-        if ( (this->cputype == CPU_TYPE_I386) && (strcmp(info.sectName, "__image_info") == 0) && (strcmp(info.segInfo.segName, "__OBJC") == 0) ) {
+        if ( (this->cputype == CPU_TYPE_I386) && (info.sectionName == "__image_info") && (info.segmentName == "__OBJC") ) {
             result = true;
             stop = true;
         }
@@ -1138,11 +553,19 @@
     return result;
 }
 
+bool MachOFile::hasConstObjCSection() const
+{
+    return hasSection("__DATA_CONST", "__objc_selrefs")
+        || hasSection("__DATA_CONST", "__objc_classrefs")
+        || hasSection("__DATA_CONST", "__objc_protorefs")
+        || hasSection("__DATA_CONST", "__objc_superrefs");
+}
+
 bool MachOFile::hasSection(const char* segName, const char* sectName) const
 {
     __block bool result = false;
-    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
-        if ( (strcmp(info.segInfo.segName, segName) == 0) && (strcmp(info.sectName, sectName) == 0) ) {
+    forEachSection(^(const Header::SectionInfo& info, bool& stop) {
+        if ( (info.segmentName == segName) && (info.sectionName == sectName) ) {
             result = true;
             stop = true;
         }
@@ -1150,68 +573,11 @@
     return result;
 }
 
-const char* MachOFile::installName() const
-{
-    const char*  name;
-    uint32_t     compatVersion;
-    uint32_t     currentVersion;
-    if ( getDylibInstallName(&name, &compatVersion, &currentVersion) )
-        return name;
-    return nullptr;
-}
-
-bool MachOFile::getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const
-{
-    Diagnostics diag;
-    __block bool found = false;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( (cmd->cmd == LC_ID_DYLIB) || (cmd->cmd == LC_ID_DYLINKER) ) {
-            const dylib_command*  dylibCmd = (dylib_command*)cmd;
-            *compatVersion  = dylibCmd->dylib.compatibility_version;
-            *currentVersion = dylibCmd->dylib.current_version;
-            *installName    = (char*)dylibCmd + dylibCmd->dylib.name.offset;
-            found = true;
-            stop = true;
-        }
-    });
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-    return found;
-}
-
-bool MachOFile::getUuid(uuid_t uuid) const
-{
-    Diagnostics diag;
-    __block bool found = false;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_UUID ) {
-            const uuid_command* uc = (const uuid_command*)cmd;
-            memcpy(uuid, uc->uuid, sizeof(uuid_t));
-            found = true;
-            stop = true;
-        }
-    });
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-    if ( !found )
-        bzero(uuid, sizeof(uuid_t));
-    return found;
-}
-
-UUID MachOFile::uuid() const {
-    Diagnostics diag;
-    __block UUID result;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_UUID ) {
-            const uuid_command* uc = (const uuid_command*)cmd;
-            result = UUID(uc->uuid);
-            stop = true;
-        }
-    });
-    diag.assertNoError();
-    return result;
-}
-
 void MachOFile::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const
 {
+    if ( this->isDyld() )
+        return;
+
     Diagnostics       diag;
     __block unsigned  count   = 0;
     __block bool      stopped = false;
@@ -1232,21 +598,32 @@
             break;
         }
     });
+    (void)count;
+    (void)stopped;
 #if !BUILDING_SHARED_CACHE_UTIL && !BUILDING_DYLDINFO && !BUILDING_UNIT_TESTS
     // everything must link with something
     if ( (count == 0) && !stopped ) {
         // The dylibs that make up libSystem can link with nothing
         // except for dylibs in libSystem.dylib which are ok to link with nothing (they are on bottom)
+        const Header* hdr = (const Header*)this;
 #if TARGET_OS_EXCLAVEKIT
-        if ( !this->isDylib() || (strncmp(this->installName(), "/System/ExclaveKit/usr/lib/system/", 34) != 0) )
+        if ( !this->isDylib() || (strncmp(hdr->installName(), "/System/ExclaveKit/usr/lib/system/", 34) != 0) )
             callback("/System/ExclaveKit/usr/lib/libSystem.dylib", false, false, false, 0x00010000, 0x00010000, stopped);
 #else
-        if ( this->builtForPlatform(Platform::driverKit, true) ) {
-            if ( !this->isDylib() || (strncmp(this->installName(), "/System/DriverKit/usr/lib/system/", 33) != 0) )
+        if ( hdr->builtForPlatform(Platform::driverKit, true) ) {
+            if ( !this->isDylib() || (strncmp(hdr->installName(), "/System/DriverKit/usr/lib/system/", 33) != 0) )
                 callback("/System/DriverKit/usr/lib/libSystem.B.dylib", false, false, false, 0x00010000, 0x00010000, stopped);
         }
+        else if (   hdr->builtForPlatform(Platform::macOS_exclaveKit, true)
+                 || hdr->builtForPlatform(Platform::iOS_exclaveKit, true)
+                 || hdr->builtForPlatform(Platform::tvOS_exclaveKit, true)
+                 || hdr->builtForPlatform(Platform::watchOS_exclaveKit, true)
+                 || hdr->builtForPlatform(Platform::visionOS_exclaveKit, true) ) {
+            // do nothing for ExclaveKit dylibs
+            // FIXME: only allow this behavior on internal builds
+        }
         else {
-            if ( !this->isDylib() || (strncmp(this->installName(), "/usr/lib/system/", 16) != 0) )
+            if ( !this->isDylib() || (strncmp(hdr->installName(), "/usr/lib/system/", 16) != 0) )
                 callback("/usr/lib/libSystem.B.dylib", false, false, false, 0x00010000, 0x00010000, stopped);
         }
 #endif // TARGET_OS_EXCLAVEKIT
@@ -1254,88 +631,6 @@
 #endif // !BUILDING_SHARED_CACHE_UTIL && !BUILDING_DYLDINFO && !BUILDING_UNIT_TESTS
     diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
 }
-
-void MachOFile::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
-{
-    Diagnostics diag;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-         if ( cmd->cmd == LC_DYLD_ENVIRONMENT ) {
-            const dylinker_command* envCmd = (dylinker_command*)cmd;
-            const char* keyEqualsValue = (char*)envCmd + envCmd->name.offset;
-            // only process variables that start with DYLD_ and end in _PATH
-            if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
-                const char* equals = strchr(keyEqualsValue, '=');
-                if ( equals != NULL ) {
-                    if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
-                        callback(keyEqualsValue, stop);
-                    }
-                }
-            }
-        }
-    });
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-}
-
-bool MachOFile::enforceCompatVersion() const
-{
-    __block bool result = true;
-    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
-        switch ( platform ) {
-            case Platform::macOS:
-                if ( minOS >= 0x000A0E00 )  // macOS 10.14
-                    result = false;
-                break;
-            case Platform::iOS:
-            case Platform::tvOS:
-            case Platform::iOS_simulator:
-            case Platform::tvOS_simulator:
-                if ( minOS >= 0x000C0000 )  // iOS 12.0
-                    result = false;
-                break;
-            case Platform::watchOS:
-            case Platform::watchOS_simulator:
-                if ( minOS >= 0x00050000 )  // watchOS 5.0
-                    result = false;
-                break;
-            case Platform::bridgeOS:
-                if ( minOS >= 0x00030000 )  // bridgeOS 3.0
-                    result = false;
-                break;
-            case Platform::driverKit:
-            case Platform::iOSMac:
-                result = false;
-                break;
-            case Platform::unknown:
-                break;
-        }
-    });
-    return result;
-}
-
-const thread_command* MachOFile::unixThreadLoadCommand() const {
-    Diagnostics diag;
-    __block const thread_command* command = nullptr;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_UNIXTHREAD ) {
-            command = (const thread_command*)cmd;
-            stop = true;
-        }
-    });
-    return command;
-}
-
-const linkedit_data_command* MachOFile::chainedFixupsCmd() const {
-    Diagnostics diag;
-    __block const linkedit_data_command* command = nullptr;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_DYLD_CHAINED_FIXUPS ) {
-            command = (const linkedit_data_command*)cmd;
-            stop = true;
-        }
-    });
-    return command;
-}
-
 
 uint32_t MachOFile::entryAddrRegisterIndexForThreadCmd() const
 {
@@ -1371,249 +666,14 @@
     return use64BitEntryRegs() ? regs64[index] : regs32[index];
 }
 
-
-bool MachOFile::getEntry(uint64_t& offset, bool& usesCRT) const
-{
-    Diagnostics diag;
-    offset = 0;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_MAIN ) {
-            entry_point_command* mainCmd = (entry_point_command*)cmd;
-            usesCRT = false;
-            offset = mainCmd->entryoff;
-            stop = true;
-        }
-        else if ( cmd->cmd == LC_UNIXTHREAD ) {
-            stop = true;
-            usesCRT = true;
-            uint64_t startAddress = entryAddrFromThreadCmd((thread_command*)cmd);
-            offset = startAddress - preferredLoadAddress();
-        }
-    });
-    return (offset != 0);
-}
-
-
-void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const
-{
-    Diagnostics diag;
-    const bool  intel32  = (this->cputype == CPU_TYPE_I386);
-    __block uint32_t segIndex = 0;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_SEGMENT_64 ) {
-            const segment_command_64* segCmd = (segment_command_64*)cmd;
-            uint64_t sizeOfSections = segCmd->vmsize;
-            uint8_t p2align = 0;
-            const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
-            const section_64* const sectionsEnd   = &sectionsStart[segCmd->nsects];
-            for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
-                if ( sect->align > p2align )
-                    p2align = sect->align;
-            }
-            SegmentInfo info;
-            info.fileOffset        = segCmd->fileoff;
-            info.fileSize          = segCmd->filesize;
-            info.vmAddr            = segCmd->vmaddr;
-            info.vmSize            = segCmd->vmsize;
-            info.sizeOfSections    = sizeOfSections;
-            info.segName           = segCmd->segname;
-            info.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
-            info.protections       = segCmd->initprot;
-            info.textRelocs        = false;
-            info.readOnlyData      = ((segCmd->flags & SG_READ_ONLY) != 0);
-            info.isProtected       = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
-            info.hasZeroFill       = (segCmd->initprot == 3) && (segCmd->filesize < segCmd->vmsize);
-            info.p2align           = p2align;
-            info.segIndex          = segIndex;
-            callback(info, stop);
-            ++segIndex;
-        }
-        else if ( cmd->cmd == LC_SEGMENT ) {
-            const segment_command* segCmd = (segment_command*)cmd;
-            uint64_t sizeOfSections = segCmd->vmsize;
-            uint8_t p2align = 0;
-            bool  hasTextRelocs = false;
-            const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
-            const section* const sectionsEnd   = &sectionsStart[segCmd->nsects];
-            for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
-                if ( sect->align > p2align )
-                    p2align = sect->align;
-                if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
-                    hasTextRelocs = true;
-           }
-            SegmentInfo info;
-            info.fileOffset        = segCmd->fileoff;
-            info.fileSize          = segCmd->filesize;
-            info.vmAddr            = segCmd->vmaddr;
-            info.vmSize            = segCmd->vmsize;
-            info.sizeOfSections    = sizeOfSections;
-            info.segName           = segCmd->segname;
-            info.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
-            info.protections       = segCmd->initprot;
-            info.textRelocs        = intel32 && !info.writable() && hasTextRelocs;
-            info.readOnlyData      = ((segCmd->flags & SG_READ_ONLY) != 0);
-            info.isProtected       = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
-            info.hasZeroFill       = (segCmd->initprot == 3) && (segCmd->filesize < segCmd->vmsize);
-            info.p2align           = p2align;
-            info.segIndex          = segIndex;
-            callback(info, stop);
-            ++segIndex;
-        }
-    });
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-}
-
-uint64_t MachOFile::preferredLoadAddress() const
-{
-    __block uint64_t textVmAddr = 0;
-    forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        if ( strcmp(info.segName, "__TEXT") == 0 ) {
-            textVmAddr = info.vmAddr;
-            stop = true;
-        }
-    });
-    return textVmAddr;
-}
-
-void MachOFile::forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const
-{
-    Diagnostics diag;
-    BLOCK_ACCCESSIBLE_ARRAY(char, sectNameCopy, 20);  // read as:  char sectNameCopy[20];
-    const bool intel32 = (this->cputype == CPU_TYPE_I386);
-    __block uint32_t segIndex = 0;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        SectionInfo sectInfo;
-        if ( cmd->cmd == LC_SEGMENT_64 ) {
-            const segment_command_64* segCmd = (segment_command_64*)cmd;
-            uint64_t sizeOfSections = segCmd->vmsize;
-            uint8_t p2align = 0;
-            const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
-            const section_64* const sectionsEnd   = &sectionsStart[segCmd->nsects];
-            for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
-                if ( sect->align > p2align )
-                    p2align = sect->align;
-            }
-            sectInfo.segInfo.fileOffset        = segCmd->fileoff;
-            sectInfo.segInfo.fileSize          = segCmd->filesize;
-            sectInfo.segInfo.vmAddr            = segCmd->vmaddr;
-            sectInfo.segInfo.vmSize            = segCmd->vmsize;
-            sectInfo.segInfo.sizeOfSections    = sizeOfSections;
-            sectInfo.segInfo.segName           = segCmd->segname;
-            sectInfo.segInfo.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
-            sectInfo.segInfo.protections       = segCmd->initprot;
-            sectInfo.segInfo.textRelocs        = false;
-            sectInfo.segInfo.readOnlyData      = ((segCmd->flags & SG_READ_ONLY) != 0);
-            sectInfo.segInfo.isProtected       = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
-            sectInfo.segInfo.p2align           = p2align;
-            sectInfo.segInfo.segIndex          = segIndex;
-            for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
-                const char* sectName = sect->sectname;
-                if ( sectName[15] != '\0' ) {
-                    strlcpy(sectNameCopy, sectName, 17);
-                    sectName = sectNameCopy;
-                }
-                bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
-                sectInfo.sectName       = sectName;
-                sectInfo.sectFileOffset = sect->offset;
-                sectInfo.sectFlags      = sect->flags;
-                sectInfo.sectAddr       = sect->addr;
-                sectInfo.sectSize       = sect->size;
-                sectInfo.sectAlignP2    = sect->align;
-                sectInfo.reserved1      = sect->reserved1;
-                sectInfo.reserved2      = sect->reserved2;
-                callback(sectInfo, malformedSectionRange, stop);
-            }
-            ++segIndex;
-        }
-        else if ( cmd->cmd == LC_SEGMENT ) {
-            const segment_command* segCmd = (segment_command*)cmd;
-            uint64_t sizeOfSections = segCmd->vmsize;
-            uint8_t p2align = 0;
-            bool  hasTextRelocs = false;
-            const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
-            const section* const sectionsEnd   = &sectionsStart[segCmd->nsects];
-            for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
-                if ( sect->align > p2align )
-                    p2align = sect->align;
-                if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
-                    hasTextRelocs = true;
-            }
-            sectInfo.segInfo.fileOffset        = segCmd->fileoff;
-            sectInfo.segInfo.fileSize          = segCmd->filesize;
-            sectInfo.segInfo.vmAddr            = segCmd->vmaddr;
-            sectInfo.segInfo.vmSize            = segCmd->vmsize;
-            sectInfo.segInfo.sizeOfSections    = sizeOfSections;
-            sectInfo.segInfo.segName           = segCmd->segname;
-            sectInfo.segInfo.loadCommandOffset = (uint32_t)((uint8_t*)segCmd - (uint8_t*)this);
-            sectInfo.segInfo.protections       = segCmd->initprot;
-            sectInfo.segInfo.textRelocs        = intel32 && !sectInfo.segInfo.writable() && hasTextRelocs;
-            sectInfo.segInfo.readOnlyData      = ((segCmd->flags & SG_READ_ONLY) != 0);
-            sectInfo.segInfo.isProtected       = (segCmd->flags & SG_PROTECTED_VERSION_1) ? 1 : 0;
-            sectInfo.segInfo.p2align           = p2align;
-            sectInfo.segInfo.segIndex          = segIndex;
-            for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) {
-                const char* sectName = sect->sectname;
-                if ( sectName[15] != '\0' ) {
-                    strlcpy(sectNameCopy, sectName, 17);
-                    sectName = sectNameCopy;
-                }
-                bool malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
-                sectInfo.sectName       = sectName;
-                sectInfo.sectFileOffset = sect->offset;
-                sectInfo.sectFlags      = sect->flags;
-                sectInfo.sectAddr       = sect->addr;
-                sectInfo.sectSize       = sect->size;
-                sectInfo.sectAlignP2    = sect->align;
-                sectInfo.reserved1      = sect->reserved1;
-                sectInfo.reserved2      = sect->reserved2;
-                callback(sectInfo, malformedSectionRange, stop);
-            }
-            ++segIndex;
-        }
-    });
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-}
-
-void MachOFile::forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const
-{
-    const unsigned ptrSize   = pointerSize();
-    const unsigned entrySize = 2 * ptrSize;
-    forEachSection(^(const MachOFile::SectionInfo& info, bool malformedSectionRange, bool &stop) {
-        if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && ((strncmp(info.segInfo.segName, "__DATA", 6) == 0) || strncmp(info.segInfo.segName, "__AUTH", 6) == 0)) ) {
-            if ( info.sectSize % entrySize != 0 ) {
-                diag.error("interposing section %s/%s has bad size", info.segInfo.segName, info.sectName);
-                stop = true;
-                return;
-            }
-            if ( malformedSectionRange ) {
-                diag.error("interposing section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
-                stop = true;
-                return;
-            }
-            if ( (info.sectAddr % ptrSize) != 0 ) {
-                diag.error("interposing section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
-                stop = true;
-                return;
-            }
-            handler(info.sectAddr - preferredLoadAddress(), info.sectSize, stop);
-        }
-    });
-}
-
-bool MachOFile::isRestricted() const
-{
-    __block bool result = false;
-    forEachSection(^(const MachOFile::SectionInfo& info, bool malformedSectionRange, bool &stop) {
-        if ( (strcmp(info.segInfo.segName, "__RESTRICT") == 0) && (strcmp(info.sectName, "__restrict") == 0) ) {
-            result = true;
-            stop = true;
-        }
-    });
-    return result;
+void MachOFile::forEachSection(void (^callback)(const Header::SectionInfo&, bool& stop)) const
+{
+    ((const Header*)this)->forEachSection(callback);
+}
+
+void MachOFile::forEachSection(void (^callback)(const Header::SegmentInfo&, const Header::SectionInfo&, bool& stop)) const
+{
+    ((const Header*)this)->forEachSection(callback);
 }
 
 bool MachOFile::hasWeakDefs() const
@@ -1626,12 +686,7 @@
     return (this->flags & MH_BINDS_TO_WEAK);
 }
 
-bool MachOFile::hasThreadLocalVariables() const
-{
-    return (this->flags & MH_HAS_TLV_DESCRIPTORS);
-}
-
-#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS || BUILDING_UNIT_TESTS || BUILDING_DYLD_SYMBOLS_CACHE
 static bool endsWith(const char* str, const char* suffix)
 {
     size_t strLen    = strlen(str);
@@ -1639,20 +694,6 @@
     if ( strLen < suffixLen )
         return false;
     return (strcmp(&str[strLen-suffixLen], suffix) == 0);
-}
-
-bool MachOFile::isSharedCacheEligiblePath(const char* dylibName) {
-    return (   (strncmp(dylibName, "/usr/lib/", 9) == 0)
-            || (strncmp(dylibName, "/System/Library/", 16) == 0)
-            || (strncmp(dylibName, "/System/iOSSupport/usr/lib/", 27) == 0)
-            || (strncmp(dylibName, "/System/iOSSupport/System/Library/", 34) == 0)
-            || (strncmp(dylibName, "/Library/Apple/usr/lib/", 23) == 0)
-            || (strncmp(dylibName, "/Library/Apple/System/Library/", 30) == 0)
-            || (strncmp(dylibName, "/System/DriverKit/", 18) == 0)
-            || (strncmp(dylibName, "/System/Cryptexes/OS/usr/lib/", 29) == 0)
-            || (strncmp(dylibName, "/System/Cryptexes/OS/System/Library/", 36) == 0)
-            || (strncmp(dylibName, "/System/Cryptexes/OS/System/iOSSupport/usr/lib/", 47) == 0)
-            || (strncmp(dylibName, "/System/Cryptexes/OS/System/iOSSupport/System/Library/", 54) == 0));
 }
 
 static bool startsWith(const char* buffer, const char* valueToFind) {
@@ -1701,45 +742,125 @@
     return false;
 }
 
-// HACK: Remove this function.  Its only here until we can handle cache overflow
-static bool platformExcludesSharedCache_sim(const char* installName) {
-    if ( startsWith(installName, "/System/Library/PrivateFrameworks/iWorkImport.framework/") )
-        return true;
-    if ( startsWith(installName, "/System/Library/PrivateFrameworks/News") )
-        return true;
-    if ( strcmp(installName, "/System/Library/PrivateFrameworks/StocksUI.framework/StocksUI") == 0 )
-        return true;
-    if ( strcmp(installName, "/System/Library/PrivateFrameworks/NewsUI.framework/NewsUI") == 0 )
-        return true;
-    if ( strcmp(installName, "/System/Library/PrivateFrameworks/CompassUI.framework/CompassUI") == 0 )
-        return true;
-    if ( strcmp(installName, "/System/Library/PrivateFrameworks/WeatherUI.framework/WeatherUI") == 0 )
-        return true;
-    if ( strcmp(installName, "/System/Library/PrivateFrameworks/NewsUI2.framework/NewsUI2") == 0 )
-        return true;
-    if ( strcmp(installName, "/System/Library/PrivateFrameworks/MLCompilerOS.framework/MLCompilerOS") == 0 )
-        return true;
-    if ( strcmp(installName, "/System/Library/PrivateFrameworks/HomeKitDaemon.framework/HomeKitDaemon") == 0 )
-        return true;
-    if ( strcmp(installName, "/System/Library/PrivateFrameworks/HomeKitDaemonLegacy.framework/HomeKitDaemonLegacy") == 0 )
-        return true;
-    return false;
-}
-
 // Returns true if the current platform requires that this install name be excluded from the shared cache
 // Note that this overrides any exclusion from anywhere else.
 static bool platformExcludesSharedCache(Platform platform, const char* installName) {
-    if ( MachOFile::isSimulatorPlatform(platform) )
-        return platformExcludesSharedCache_sim(installName);
-    if ( (platform == dyld3::Platform::macOS) || (platform == dyld3::Platform::iOSMac) )
+    if ( (platform == Platform::macOS) || (platform == Platform::macCatalyst) || (platform == Platform::zippered) )
         return platformExcludesSharedCache_macOS(installName);
     // Everything else is based on iOS so just use that value
     return platformExcludesSharedCache_iOS(installName);
 }
 
-bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
-{
-    if ( !isSharedCacheEligiblePath(path) ) {
+#if !BUILDING_DYLD
+
+bool MachOFile::addendsExceedPatchTableLimit(Diagnostics& diag, mach_o::Fixups fixups) const
+{
+    // rdar://122906481 (Shared cache builder - explicitly model dylibs without a need for a patch table)
+    if ( strcmp(((const Header*)this)->installName(), "/usr/lib/libswiftPrespecialized.dylib") == 0 )
+        return false;
+
+    const bool     is64bit = is64();
+    const uint64_t tooLargeRegularAddend = 1 << 23;
+    const uint64_t tooLargeAuthAddend = 1 << 5;
+    __block bool addendTooLarge = false;
+    if ( this->hasChainedFixups() ) {
+
+        // with chained fixups, addends can be in the import table or embedded in a bind pointer
+        __block std::vector<uint64_t> targetAddends;
+        fixups.forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+            if ( is64bit )
+                addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
+            targetAddends.push_back(addend);
+        });
+        // check each pointer for embedded addend
+        fixups.withChainStarts(diag, ^(const dyld_chained_starts_in_image* starts) {
+            fixups.forEachFixupInAllChains(diag, starts, false, ^(mach_o::ChainedFixupPointerOnDisk* fixupLoc, uint64_t fixupSegmentOffset, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+                switch (segInfo->pointer_format) {
+                    case DYLD_CHAINED_PTR_ARM64E:
+                    case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+                        if ( fixupLoc->arm64e.bind.bind ) {
+                            uint32_t ordinal = fixupLoc->arm64e.bind.ordinal;
+                            uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
+                            if ( fixupLoc->arm64e.bind.auth ) {
+                                if ( addend >= tooLargeAuthAddend ) {
+                                    addendTooLarge = true;
+                                    stop = true;
+                                }
+                            } else {
+                                addend += fixupLoc->arm64e.signExtendedAddend();
+                                if ( addend >= tooLargeRegularAddend ) {
+                                    addendTooLarge = true;
+                                    stop = true;
+                                }
+                            }
+                        }
+                        break;
+                    case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+                        if ( fixupLoc->arm64e.bind24.bind ) {
+                            uint32_t ordinal = fixupLoc->arm64e.bind24.ordinal;
+                            uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
+                            if ( fixupLoc->arm64e.bind24.auth ) {
+                                if ( addend >= tooLargeAuthAddend ) {
+                                    addendTooLarge = true;
+                                    stop = true;
+                                }
+                            } else {
+                                addend += fixupLoc->arm64e.signExtendedAddend();
+                                if ( addend >= tooLargeRegularAddend ) {
+                                    addendTooLarge = true;
+                                    stop = true;
+                                }
+                            }
+                        }
+                        break;
+                    case DYLD_CHAINED_PTR_64:
+                    case DYLD_CHAINED_PTR_64_OFFSET: {
+                        if ( fixupLoc->generic64.rebase.bind ) {
+                            uint32_t ordinal = fixupLoc->generic64.bind.ordinal;
+                            uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
+                            addend += fixupLoc->generic64.bind.addend;
+                            if ( addend >= tooLargeRegularAddend ) {
+                                addendTooLarge = true;
+                                stop = true;
+                            }
+                        }
+                        break;
+                    }
+                    case DYLD_CHAINED_PTR_32:
+                        if ( fixupLoc->generic32.bind.bind ) {
+                            uint32_t ordinal = fixupLoc->generic32.bind.ordinal;
+                            uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
+                            addend += fixupLoc->generic32.bind.addend;
+                            if ( addend >= tooLargeRegularAddend ) {
+                                addendTooLarge = true;
+                                stop = true;
+                            }
+                        }
+                        break;
+                }
+            });
+        });
+    }
+    else {
+        // scan bind opcodes for large addend
+        auto handler = ^(const mach_o::Fixups::BindTargetInfo &info, bool &stop) {
+            uint64_t addend = info.addend;
+            if ( is64bit )
+                addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
+            if ( addend >= tooLargeRegularAddend ) {
+                addendTooLarge = true;
+                stop = true;
+            }
+        };
+        fixups.forEachBindTarget_Opcodes(diag, true, handler, handler);
+    }
+
+    return addendTooLarge;
+}
+
+bool MachOFile::canBePlacedInDyldCache(const char* path, bool checkObjC, void (^failureReason)(const char* format, ...)) const
+{
+    if ( !Header::isSharedCacheEligiblePath(path) ) {
         // Dont spam the user with an error about paths when we know these are never eligible.
         return false;
     }
@@ -1751,7 +872,7 @@
     }
 
 
-    const char* dylibName = installName();
+    const char* dylibName = ((const Header*)this)->installName();
     if ( dylibName[0] != '/' ) {
         failureReason("install name not an absolute path");
         // Don't continue as we don't want to spam the log with errors we don't need.
@@ -1770,15 +891,9 @@
         return false;
     }
 
-    __block bool platformExcludedFile = false;
-    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
-        if ( platformExcludedFile )
-            return;
-        if ( platformExcludesSharedCache(platform, dylibName) ) {
-            platformExcludedFile = true;
-            return;
-        }
-    });
+    mach_o::PlatformAndVersions pvs = ((const Header*)this)->platformAndVersions();
+    bool platformExcludedFile = platformExcludesSharedCache(pvs.platform, dylibName);
+    
     if ( platformExcludedFile ) {
         failureReason("install name is not shared cache eligible on platform");
         return false;
@@ -1792,19 +907,26 @@
 
     // don't put debug variants into dyld cache
     if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_asan.dylib")
-        || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) {
+        || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "_asan")
+        || endsWith(path, "/CoreADI") ) {
         failureReason("Variant image");
         return false;
     }
 
     // dylib must have extra info for moving DATA and TEXT segments apart
     __block bool hasExtraInfo = false;
+    __block bool hasSplitSegMarker = false;
     __block bool hasDyldInfo = false;
     __block bool hasExportTrie = false;
     __block Diagnostics diag;
     forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
-            hasExtraInfo = true;
+        if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO ) {
+            const linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
+            if ( sigCmd->datasize == 0 )
+                hasSplitSegMarker = true;
+            else
+                hasExtraInfo = true;
+        }
         if ( cmd->cmd == LC_DYLD_INFO_ONLY )
             hasDyldInfo = true;
         if ( cmd->cmd == LC_DYLD_EXPORTS_TRIE )
@@ -1819,7 +941,10 @@
             if ( ignorePath == path )
                 return false;
         }
-        failureReason("Missing split seg info");
+        if ( hasSplitSegMarker )
+            failureReason("Dylib explicitly linked with '-not_for_dyld_shared_cache'");
+        else
+            failureReason("Missing split seg info");
         return false;
     }
     if ( !hasDyldInfo && !hasExportTrie ) {
@@ -1828,29 +953,30 @@
     }
 
     // dylib can only depend on other dylibs in the shared cache
-    __block bool allDepPathsAreGood = true;
+    __block const char* badDep = nullptr;
     forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
         // Skip weak links.  They are allowed to be missing
         if ( isWeak )
             return;
-        if ( !isSharedCacheEligiblePath(loadPath) ) {
-            allDepPathsAreGood = false;
+        if ( !Header::isSharedCacheEligiblePath(loadPath) ) {
+            badDep = loadPath;
             stop = true;
         }
     });
-    if ( !allDepPathsAreGood ) {
-        failureReason("Depends on dylibs ineligable for dyld cache");
+    if ( badDep != nullptr ) {
+        failureReason("Depends on dylibs ineligible for dyld cache '%s'.  (cache dylibs must start /usr/lib or /System/Library or similar)",
+                      badDep);
         return false;
     }
 
     // dylibs with interposing info cannot be in cache
-    if ( hasInterposingTuples() ) {
+    if ( ((const Header*)this)->hasInterposingTuples() ) {
         failureReason("Has interposing tuples");
         return false;
     }
 
     // Temporarily kick out swift binaries out of dyld cache on watchOS simulators as they have missing split seg
-    if ( (this->cputype == CPU_TYPE_I386) && builtForPlatform(Platform::watchOS_simulator) ) {
+    if ( (this->cputype == CPU_TYPE_I386) && ((const Header*)this)->builtForPlatform(Platform::watchOS_simulator) ) {
         if ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) {
             failureReason("i386 swift binary");
             return false;
@@ -1876,12 +1002,22 @@
         if ( layout.isSwiftLibrary() && splitSeg.isV1() )
             return;
 
+        // arm64e requires signed class ROs
+        if ( isArch("arm64e") ) {
+            if ( std::optional<uint32_t> flags = layout.getObjcInfoFlags(); flags.has_value() ) {
+                if ( (flags.value() & mach_o::ObjCImageInfo::OBJC_IMAGE_SIGNED_CLASS_RO) == 0 ) {
+                    failureReason("arm64e binaries must have signed Objective-C class_ro_t pointers");
+                    return;
+                }
+            }
+        }
+
         if ( splitSeg.isV1() ) {
             // Split seg v1 can only support 1 __DATA, and no other writable segments
             __block bool foundBadSegment = false;
-            forEachSegment(^(const SegmentInfo& info, bool& stop) {
-                if ( info.protections == (VM_PROT_READ | VM_PROT_WRITE) ) {
-                    if ( strcmp(info.segName, "__DATA") == 0 )
+            ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
+                if ( info.initProt == (VM_PROT_READ | VM_PROT_WRITE) ) {
+                    if ( info.segmentName == "__DATA" )
                         return;
 
                     failureReason("RW segments other than __DATA requires split seg v2");
@@ -1896,101 +1032,7 @@
 
         // <rdar://problem/57769033> dyld_cache_patchable_location only supports addend in range 0..31
         // rdar://96164956 (dyld needs to support arbitrary addends in cache patch table)
-        const bool is64bit = is64();
-        __block bool addendTooLarge = false;
-        const uint64_t tooLargeRegularAddend = 1 << 23;
-        const uint64_t tooLargeAuthAddend = 1 << 5;
-        if ( this->hasChainedFixups() ) {
-
-            // with chained fixups, addends can be in the import table or embedded in a bind pointer
-            __block std::vector<uint64_t> targetAddends;
-            fixups.forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
-                if ( is64bit )
-                    addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
-                targetAddends.push_back(addend);
-            });
-            // check each pointer for embedded addend
-            fixups.withChainStarts(diag, ^(const dyld_chained_starts_in_image* starts) {
-                fixups.forEachFixupInAllChains(diag, starts, false, ^(mach_o::ChainedFixupPointerOnDisk* fixupLoc, uint64_t fixupSegmentOffset, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
-                    switch (segInfo->pointer_format) {
-                        case DYLD_CHAINED_PTR_ARM64E:
-                        case DYLD_CHAINED_PTR_ARM64E_USERLAND:
-                            if ( fixupLoc->arm64e.bind.bind ) {
-                                uint64_t ordinal = fixupLoc->arm64e.bind.ordinal;
-                                uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
-                                if ( fixupLoc->arm64e.bind.auth ) {
-                                    if ( addend >= tooLargeAuthAddend ) {
-                                        addendTooLarge = true;
-                                        stop = true;
-                                    }
-                                } else {
-                                    addend += fixupLoc->arm64e.signExtendedAddend();
-                                    if ( addend >= tooLargeRegularAddend ) {
-                                        addendTooLarge = true;
-                                        stop = true;
-                                    }
-                                }
-                            }
-                            break;
-                        case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
-                            if ( fixupLoc->arm64e.bind24.bind ) {
-                                uint64_t ordinal = fixupLoc->arm64e.bind24.ordinal;
-                                uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
-                                if ( fixupLoc->arm64e.bind24.auth ) {
-                                    if ( addend >= tooLargeAuthAddend ) {
-                                        addendTooLarge = true;
-                                        stop = true;
-                                    }
-                                } else {
-                                    addend += fixupLoc->arm64e.signExtendedAddend();
-                                    if ( addend >= tooLargeRegularAddend ) {
-                                        addendTooLarge = true;
-                                        stop = true;
-                                    }
-                                }
-                            }
-                            break;
-                        case DYLD_CHAINED_PTR_64:
-                        case DYLD_CHAINED_PTR_64_OFFSET: {
-                            if ( fixupLoc->generic64.rebase.bind ) {
-                                uint64_t ordinal = fixupLoc->generic64.bind.ordinal;
-                                uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
-                                addend += fixupLoc->generic64.bind.addend;
-                                if ( addend >= tooLargeRegularAddend ) {
-                                    addendTooLarge = true;
-                                    stop = true;
-                                }
-                            }
-                            break;
-                        }
-                        case DYLD_CHAINED_PTR_32:
-                            if ( fixupLoc->generic32.bind.bind ) {
-                                uint64_t ordinal = fixupLoc->generic32.bind.ordinal;
-                                uint64_t addend = (ordinal < targetAddends.size()) ? targetAddends[ordinal] : 0;
-                                addend += fixupLoc->generic32.bind.addend;
-                                if ( addend >= tooLargeRegularAddend ) {
-                                    addendTooLarge = true;
-                                    stop = true;
-                                }
-                            }
-                            break;
-                    }
-                });
-            });
-        }
-        else {
-            // scan bind opcodes for large addend
-            auto handler = ^(const mach_o::Fixups::BindTargetInfo &info, bool &stop) {
-                uint64_t addend = info.addend;
-                if ( is64bit )
-                    addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
-                if ( addend >= tooLargeRegularAddend ) {
-                    addendTooLarge = true;
-                    stop = true;
-                }
-            };
-            fixups.forEachBindTarget_Opcodes(diag, true, handler, handler);
-        }
+        bool addendTooLarge = addendsExceedPatchTableLimit(diag, fixups);
         if ( addendTooLarge ) {
             failureReason("bind addend too large");
             return;
@@ -1998,7 +1040,7 @@
 
         if ( (isArch("x86_64") || isArch("x86_64h")) ) {
             __block bool rebasesOk = true;
-            uint64_t startVMAddr = preferredLoadAddress();
+            uint64_t startVMAddr = ((const Header*)this)->preferredLoadAddress();
             uint64_t endVMAddr = startVMAddr + mappedSize();
             fixups.forEachRebase(diag, ^(uint64_t runtimeOffset, uint64_t rebasedValue, bool &stop) {
                 // We allow TBI for x86_64 dylibs, but then require that the remainder of the offset
@@ -2078,20 +1120,189 @@
         passedLinkeditChecks = true;
     });
 
-    return passedLinkeditChecks;
-}
+    if ( !passedLinkeditChecks )
+        return false;
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+
+    // Make sure we don't have cheaper roots patching (binds in the __objc_classlist) with bind opcodes
+    if ( checkObjC && hasOpcodeFixups() && hasSection("__DATA_CONST", "__objc_classlist") ) {
+        this->withFileLayout(diag, ^(const mach_o::Layout& layout) {
+            mach_o::Fixups fixups(layout);
+
+            __block uint64_t classListStartOffset = 0;
+            __block uint64_t classListEndOffset = 0;
+            this->forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
+                if ( sectInfo.segmentName != "__DATA_CONST" )
+                    return;
+                if ( sectInfo.sectionName != "__objc_classlist" )
+                    return;
+
+                classListStartOffset = sectInfo.address - layout.textUnslidVMAddr();
+                classListEndOffset = classListStartOffset + sectInfo.size;
+                stop = true;
+            });
+
+            fixups.forEachBindLocation_Opcodes(diag, ^(uint64_t runtimeOffset, uint32_t segmentIndex, unsigned int targetIndex, bool &stop) {
+                if ( (runtimeOffset < classListStartOffset) || (runtimeOffset > classListEndOffset) )
+                    return;
+                passedLinkeditChecks = false;
+                failureReason("has opcode fixups and objc cheaper roots.  Likely has unaligned fixups");
+                stop = true;
+            }, ^(uint64_t runtimeOffset, uint32_t segmentIndex, unsigned int overrideBindTargetIndex, bool &stop) {
+            });
+        });
+
+        if ( !passedLinkeditChecks )
+            return false;
+    }
+
+    // Check there are no pointer based objc method lists in CONST segments
+    if ( checkObjC ) {
+        typedef std::pair<VMAddress, VMAddress> Range;
+        __block std::vector<Range> constRanges;
+        ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
+            if ( info.vmsize == 0 )
+                return;
+            if ( info.segmentName == "__DATA_CONST" || info.segmentName == "__AUTH_CONST" )
+                constRanges.push_back({ VMAddress(info.vmaddr), VMAddress(info.vmaddr + info.vmsize) });
+        });
+
+        if ( !constRanges.empty() ) {
+            __block objc_visitor::Visitor objcVisitor = this->makeObjCVisitor(diag);
+            if ( diag.hasError() )
+                return false;
+
+            // Returns true if the method list is bad, ie, a pointer based method list in _CONST segment
+            auto isConstPointerBasedMethodList = ^(const objc_visitor::MethodList& methodList) {
+                if ( (methodList.numMethods() == 0) || methodList.usesRelativeOffsets() )
+                    return false;
+
+                VMAddress methodListVMAddr = methodList.getVMAddress().value();
+                for ( const Range& range : constRanges ) {
+                    if ( (methodListVMAddr >= range.first) && (methodListVMAddr < range.second) )
+                        return true;
+                }
+
+                return false;
+            };
+
+            __block bool hasPointerMethodList = false;
+            objcVisitor.forEachClassAndMetaClass(^(const objc_visitor::Class& objcClass, bool& stopClass) {
+                if ( isConstPointerBasedMethodList(objcClass.getBaseMethods(objcVisitor)) ) {
+                    failureReason("has pointer based objc class method list in _CONST segment");
+                    hasPointerMethodList = true;
+                    stopClass = true;
+                }
+            });
+            if ( hasPointerMethodList )
+                return false;
+
+            objcVisitor.forEachCategory(^(const objc_visitor::Category& objcCategory, bool& stopCategory) {
+                if ( isConstPointerBasedMethodList(objcCategory.getInstanceMethods(objcVisitor)) ) {
+                    failureReason("has pointer based objc category instance method list in _CONST segment");
+                    hasPointerMethodList = true;
+                    stopCategory = true;
+                }
+                if ( isConstPointerBasedMethodList(objcCategory.getClassMethods(objcVisitor)) ) {
+                    failureReason("has pointer based objc category class method list in _CONST segment");
+                    hasPointerMethodList = true;
+                    stopCategory = true;
+                }
+            });
+            if ( hasPointerMethodList )
+                return false;
+        }
+    }
+#endif // BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+
+    return true;
+}
+
+#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+objc_visitor::Visitor MachOFile::makeObjCVisitor(Diagnostics& diag) const
+{
+    VMAddress dylibBaseAddress(((const Header*)this)->preferredLoadAddress());
+
+    __block std::vector<metadata_visitor::Segment> segments;
+    __block std::vector<uint64_t> bindTargets;
+    this->withFileLayout(diag, ^(const mach_o::Layout &layout) {
+        for ( uint32_t segIndex = 0; segIndex != layout.segments.size(); ++segIndex ) {
+            const auto& layoutSegment = layout.segments[segIndex];
+            metadata_visitor::Segment segment {
+                .startVMAddr = VMAddress(layoutSegment.vmAddr),
+                .endVMAddr = VMAddress(layoutSegment.vmAddr + layoutSegment.vmSize),
+                .bufferStart = (uint8_t*)layoutSegment.buffer,
+                .onDiskDylibChainedPointerFormat = 0,
+                .segIndex = segIndex
+            };
+            segments.push_back(std::move(segment));
+        }
+
+        // Add chained fixup info to each segment, if we have it
+        if ( this->hasChainedFixups() ) {
+            mach_o::Fixups fixups(layout);
+            fixups.withChainStarts(diag, ^(const dyld_chained_starts_in_image* starts) {
+                mach_o::Fixups::forEachFixupChainSegment(diag, starts,
+                                                         ^(const dyld_chained_starts_in_segment *segInfo, uint32_t segIndex, bool &stop) {
+                    segments[segIndex].onDiskDylibChainedPointerFormat = segInfo->pointer_format;
+                });
+            });
+        }
+
+        // ObjC patching needs the bind targets for interposable references to the classes
+        // build targets table
+        if ( this->hasChainedFixupsLoadCommand() ) {
+            mach_o::Fixups fixups(layout);
+            fixups.forEachBindTarget_ChainedFixups(diag, ^(const mach_o::Fixups::BindTargetInfo &info, bool &stop) {
+                if ( info.libOrdinal != BIND_SPECIAL_DYLIB_SELF ) {
+                    bindTargets.push_back(0);
+                    return;
+                }
+
+                mach_o::Layout::FoundSymbol foundInfo;
+                if ( !layout.findExportedSymbol(diag, info.symbolName, info.weakImport, foundInfo) ) {
+                    bindTargets.push_back(0);
+                    return;
+                }
+
+                // We only support header offsets in this dylib, as we are looking for self binds
+                // which are likely only to classes
+                if ( (foundInfo.kind != mach_o::Layout::FoundSymbol::Kind::headerOffset)
+                    || (foundInfo.foundInDylib.value() != this) ) {
+                    bindTargets.push_back(0);
+                    return;
+                }
+
+                uint64_t vmAddr = layout.textUnslidVMAddr() + foundInfo.value;
+                bindTargets.push_back(vmAddr);
+            });
+        }
+    });
+
+    std::optional<VMAddress> selectorStringsBaseAddress;
+    objc_visitor::Visitor objcVisitor(dylibBaseAddress, this,
+                                      std::move(segments), selectorStringsBaseAddress,
+                                      std::move(bindTargets));
+
+    return objcVisitor;
+}
+#endif // BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
+
+#endif // !BUILDING_DYLD
+
 
 // Returns true if the executable path is eligible for a PrebuiltLoader on the given platform.
-bool MachOFile::canHavePrebuiltExecutableLoader(dyld3::Platform platform, const std::string_view& path,
+bool MachOFile::canHavePrebuiltExecutableLoader(Platform platform, const std::string_view& path,
                                                 void (^failureReason)(const char*)) const
 {
     // For now we can't build prebuilt loaders for the simulator
-    if ( isSimulatorPlatform(platform) ) {
+    if ( platform.isSimulator() ) {
         // Don't spam with tons of messages about executables
         return false;
     }
 
-    if ( (platform == dyld3::Platform::macOS) || (platform == dyld3::Platform::iOSMac) ) {
+    if ( (platform == Platform::macOS) || (platform == Platform::macCatalyst) ) {
         // We no longer support ROSP, so skip all paths which start with the special prefix
         if ( path.starts_with("/System/Library/Templates/Data/") ) {
             // Dont spam the user with an error about paths when we know these are never eligible.
@@ -2121,15 +1332,9 @@
             failureReason("path not eligible");
             return false;
         }
-    } else {
-        // On embedded, only staged apps are excluded.  They will run from a different location at runtime
-        if ( path.find("/staged_system_apps/") != std::string::npos ) {
-            // Dont spam the user with an error about paths when we know these are never eligible.
-            return false;
-        }
-    }
-
-    if ( !hasCodeSignature() ) {
+    }
+
+    if ( !this->hasLoadCommand(LC_CODE_SIGNATURE) ) {
         failureReason("missing code signature");
         return false;
     }
@@ -2162,8 +1367,8 @@
 
         // xnu kernel cannot have a page zero
         __block bool foundPageZero = false;
-        forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) {
-            if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 ) {
+        ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo &segmentInfo, bool &stop) {
+            if ( segmentInfo.segmentName == "__PAGEZERO" ) {
                 foundPageZero = true;
                 stop = true;
             }
@@ -2219,7 +1424,7 @@
     }
 
     // dylibs with interposing info cannot be in cache
-    if ( hasInterposingTuples() ) {
+    if ( ((const Header*)this)->hasInterposingTuples() ) {
         failureReason("Has interposing tuples");
         return false;
     }
@@ -2227,8 +1432,8 @@
     // Only x86_64 is allowed to have RWX segments
     if ( !isArch("x86_64") && !isArch("x86_64h") ) {
         __block bool foundBadSegment = false;
-        forEachSegment(^(const SegmentInfo &info, bool &stop) {
-            if ( (info.protections & (VM_PROT_WRITE | VM_PROT_EXECUTE)) == (VM_PROT_WRITE | VM_PROT_EXECUTE) ) {
+        ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
+            if ( (info.initProt & (VM_PROT_WRITE | VM_PROT_EXECUTE)) == (VM_PROT_WRITE | VM_PROT_EXECUTE) ) {
                 failureReason("Segments are not allowed to be both writable and executable");
                 foundBadSegment = true;
                 stop = true;
@@ -2240,7 +1445,9 @@
 
     return true;
 }
-
+#endif // BUILDING_APP_CACHE_UTIL
+
+#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
 bool MachOFile::usesClassicRelocationsInKernelCollection() const {
     // The xnu x86_64 static executable needs to do the i386->x86_64 transition
     // so will be emitted with classic relocations
@@ -2249,146 +1456,7 @@
     }
     return false;
 }
-#endif
-
-#if BUILDING_CACHE_BUILDER || BUILDING_CACHE_BUILDER_UNIT_TESTS
-static bool platformExcludesPrebuiltClosure_macOS(const char* path) {
-    // We no longer support ROSP, so skip all paths which start with the special prefix
-    if ( startsWith(path, "/System/Library/Templates/Data/") )
-        return true;
-
-    // anything inside a .app bundle is specific to app, so should not get a prebuilt closure
-    if ( strstr(path, ".app/") != NULL )
-        return true;
-
-    return false;
-}
-
-static bool platformExcludesPrebuiltClosure_iOS(const char* path) {
-    if ( strcmp(path, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
-        return true;
-    if ( strcmp(path, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
-        return true;
-    return false;
-}
-
-// Returns true if the current platform requires that this install name be excluded from the shared cache
-// Note that this overrides any exclusion from anywhere else.
-static bool platformExcludesPrebuiltClosure(Platform platform, const char* path) {
-    if ( MachOFile::isSimulatorPlatform(platform) )
-        return false;
-    if ( (platform == dyld3::Platform::macOS) || (platform == dyld3::Platform::iOSMac) )
-        return platformExcludesPrebuiltClosure_macOS(path);
-    // Everything else is based on iOS so just use that value
-    return platformExcludesPrebuiltClosure_iOS(path);
-}
-
-bool MachOFile::canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const
-{
-    __block bool retval = true;
-
-    // only dylibs can go in cache
-    if ( (this->filetype != MH_DYLIB) && (this->filetype != MH_BUNDLE) ) {
-        retval = false;
-        failureReason("not MH_DYLIB or MH_BUNDLE");
-    }
-
-    // flat namespace files cannot go in cache
-    if ( (this->flags & MH_TWOLEVEL) == 0 ) {
-        retval = false;
-        failureReason("not built with two level namespaces");
-    }
-
-    // can only depend on other dylibs with absolute paths
-    __block bool allDepPathsAreGood = true;
-    forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
-        if ( loadPath[0] != '/' ) {
-            allDepPathsAreGood = false;
-            stop = true;
-        }
-    });
-    if ( !allDepPathsAreGood ) {
-        retval = false;
-        failureReason("depends on dylibs that are not absolute paths");
-    }
-
-    __block bool platformExcludedFile = false;
-    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
-        if ( platformExcludedFile )
-            return;
-        if ( platformExcludesPrebuiltClosure(platform, path) ) {
-            platformExcludedFile = true;
-            return;
-        }
-    });
-    if ( platformExcludedFile ) {
-        failureReason("file cannot get a prebuilt closure on this platform");
-        return false;
-    }
-
-    // dylibs with interposing info cannot have dlopen closure pre-computed
-    if ( hasInterposingTuples() ) {
-        retval = false;
-        failureReason("has interposing tuples");
-    }
-
-    // special system dylib overrides cannot have closure pre-computed
-    if ( strncmp(path, "/usr/lib/system/introspection/", 30) == 0 ) {
-        retval = false;
-        failureReason("override of OS dylib");
-    }
-
-    return retval;
-}
-#endif
-
-bool MachOFile::hasInterposingTuples() const
-{
-    __block bool hasInterposing = false;
-    Diagnostics diag;
-    forEachInterposingSection(diag, ^(uint64_t vmOffset, uint64_t vmSize, bool &stop) {
-        hasInterposing = true;
-        stop = true;
-    });
-    return hasInterposing;
-}
-
-bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
-{
-    if ( const encryption_info_command* encCmd = findFairPlayEncryptionLoadCommand() ) {
-       if ( encCmd->cryptid == 1 ) {
-            // Note: cryptid is 0 in just-built apps.  The AppStore sets cryptid to 1
-            textOffset = encCmd->cryptoff;
-            size       = encCmd->cryptsize;
-            return true;
-        }
-    }
-    textOffset = 0;
-    size = 0;
-    return false;
-}
-
-bool MachOFile::canBeFairPlayEncrypted() const
-{
-    return (findFairPlayEncryptionLoadCommand() != nullptr);
-}
-
-const encryption_info_command* MachOFile::findFairPlayEncryptionLoadCommand() const
-{
-    __block const encryption_info_command* result = nullptr;
-    Diagnostics diag;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-         if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
-            result = (encryption_info_command*)cmd;
-            stop = true;
-        }
-    });
-    if ( diag.noError() )
-        return result;
-    else
-        return nullptr;
-}
-
+#endif // BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
 
 bool MachOFile::hasLoadCommand(uint32_t cmdNum) const
 {
@@ -2401,18 +1469,6 @@
         }
     });
     return hasLC;
-}
-
-bool MachOFile::allowsAlternatePlatform() const
-{
-    __block bool result = false;
-    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
-        if ( (strcmp(info.sectName, "__allow_alt_plat") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
-            result = true;
-            stop = true;
-        }
-    });
-    return result;
 }
 
 bool MachOFile::hasChainedFixups() const
@@ -2499,6 +1555,12 @@
                         chainEnd = true;
                     else
                         chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.arm64e.rebase.next*stride);
+                    break;
+                case DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE:
+                    if ( chainContent.cache64e.regular.next == 0 )
+                        chainEnd = true;
+                    else
+                        chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.cache64e.regular.next*stride);
                     break;
                 case DYLD_CHAINED_PTR_64:
                 case DYLD_CHAINED_PTR_64_OFFSET:
@@ -2696,62 +1758,65 @@
     return result;
 }
 
-static void getArchNames(const GradedArchs& archs, bool isOSBinary, char buffer[256])
+static void getArchNames(const GradedArchitectures& archs, bool isOSBinary, char buffer[256])
 {
     buffer[0] = '\0';
-    archs.forEachArch(isOSBinary, ^(const char* archName) {
+    archs.forEachArch(isOSBinary, ^(mach_o::Architecture arch) {
         if ( buffer[0] != '\0' )
             strlcat(buffer, "' or '", 256);
-        strlcat(buffer, archName, 256);
+        strlcat(buffer, arch.name(), 256);
     });
 }
-
-const MachOFile* MachOFile::compatibleSlice(Diagnostics& diag, const void* fileContent, size_t contentSize, const char* path, Platform platform, bool isOSBinary, const GradedArchs& archs, bool internalInstall)
-{
-    const MachOFile* mf = nullptr;
-    if ( const dyld3::FatFile* ff = dyld3::FatFile::isFatFile(fileContent) ) {
-        uint64_t  sliceOffset;
-        uint64_t  sliceLen;
-        bool      missingSlice;
-        if ( ff->isFatFileWithSlice(diag, contentSize, archs, isOSBinary, sliceOffset, sliceLen, missingSlice) ) {
-            mf = (MachOFile*)((long)fileContent + sliceOffset);
+ 
+const MachOFile* MachOFile::compatibleSlice(Diagnostics& diag, uint64_t& sliceOffsetOut, uint64_t& sliceLenOut, const void* fileContent, size_t contentSize, const char* path, Platform platform, bool isOSBinary, const GradedArchitectures& archs, bool internalInstall)
+{
+    const Header* hdr = nullptr;
+    std::span<const uint8_t> content = { (const uint8_t*)fileContent, contentSize };
+    if ( const Universal* uni = Universal::isUniversal(content) ) {
+        if ( mach_o::Error err = uni->valid(content.size()) ) {
+            diag.error("%s", err.message());
+            return nullptr;
+        }
+        Universal::Slice slice;
+        if ( uni->bestSlice(archs, isOSBinary, slice) ) {
+            hdr = (const Header*)slice.buffer.data();
+            sliceLenOut = slice.buffer.size();
+            sliceOffsetOut = slice.buffer.data() - content.data();
         }
         else {
-            BLOCK_ACCCESSIBLE_ARRAY(char, gradedArchsBuf, 256);
+            char strBuf[256];
+            char gradedArchsBuf[256];
             getArchNames(archs, isOSBinary, gradedArchsBuf);
-
-            char strBuf[256];
-            diag.error("fat file, but missing compatible architecture (have '%s', need '%s')", ff->archNames(strBuf, contentSize), gradedArchsBuf);
+            diag.error("fat file, but missing compatible architecture (have '%s', need '%s')", uni->archNames(strBuf), gradedArchsBuf);
             return nullptr;
         }
     }
     else {
-        mf = (MachOFile*)fileContent;
-    }
-
-    if ( !mf->hasMachOMagic() || !mf->isMachO(diag, contentSize) ) {
-        if ( diag.noError() )
-            diag.error("not a mach-o file");
+        hdr = (const Header*)fileContent;
+        sliceLenOut = contentSize;
+        sliceOffsetOut = 0;
+    }
+
+    std::span<const uint8_t> contents{(uint8_t*)hdr, (size_t)sliceLenOut};
+    if ( !Header::isMachO(contents) ) {
+        diag.error("slice is not valid mach-o file");
         return nullptr;
     }
 
-    if ( archs.grade(mf->cputype, mf->cpusubtype, isOSBinary) == 0 ) {
-        BLOCK_ACCCESSIBLE_ARRAY(char, gradedArchsBuf, 256);
+    if ( !archs.isCompatible(hdr->arch(), isOSBinary) ) {
+        char gradedArchsBuf[256];
         getArchNames(archs, isOSBinary, gradedArchsBuf);
-        diag.error("mach-o file, but is an incompatible architecture (have '%s', need '%s')", mf->archName(), gradedArchsBuf);
+        diag.error("mach-o file, but is an incompatible architecture (have '%s', need '%s')", hdr->archName(), gradedArchsBuf);
         return nullptr;
     }
 
-    if ( !mf->loadableIntoProcess(platform, path, internalInstall) ) {
-        __block Platform havePlatform = Platform::unknown;
-        mf->forEachSupportedPlatform(^(Platform aPlat, uint32_t minOS, uint32_t sdk) {
-            havePlatform = aPlat;
-        });
-        diag.error("mach-o file (%s), but incompatible platform (have '%s', need '%s')", path, MachOFile::platformName(havePlatform), MachOFile::platformName(platform));
+    if ( !hdr->loadableIntoProcess(platform, path, internalInstall) ) {
+        Platform havePlatform = hdr->platformAndVersions().platform;
+        diag.error("mach-o file (%s), but incompatible platform (have '%s', need '%s')", path, havePlatform.name().c_str(), platform.name().c_str());
         return nullptr;
     }
 
-    return mf;
+    return (const MachOFile*)hdr;
 }
 
 const uint8_t* MachOFile::trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol)
@@ -2842,19 +1907,6 @@
     return nullptr;
 }
 
-void MachOFile::forEachRPath(void (^callback)(const char* rPath, bool& stop)) const
-{
-    Diagnostics diag;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-         if ( cmd->cmd == LC_RPATH ) {
-            const char* rpath = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
-            callback(rpath, stop);
-        }
-    });
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-}
-
-
 bool MachOFile::inCodeSection(uint32_t runtimeOffset) const
 {
     // only needed for arm64e code to know to sign pointers
@@ -2862,10 +1914,10 @@
         return false;
 
     __block bool result = false;
-    uint64_t baseAddress = this->preferredLoadAddress();
-    this->forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
-        if ( ((sectInfo.sectAddr-baseAddress) <= runtimeOffset) && (runtimeOffset < (sectInfo.sectAddr+sectInfo.sectSize-baseAddress)) ) {
-            result = ( (sectInfo.sectFlags & S_ATTR_PURE_INSTRUCTIONS) || (sectInfo.sectFlags & S_ATTR_SOME_INSTRUCTIONS) );
+    uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
+    this->forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
+        if ( ((sectInfo.address-baseAddress) <= runtimeOffset) && (runtimeOffset < (sectInfo.address+sectInfo.size-baseAddress)) ) {
+            result = ( (sectInfo.flags & S_ATTR_PURE_INSTRUCTIONS) || (sectInfo.flags & S_ATTR_SOME_INSTRUCTIONS) );
             stop = true;
         }
     });
@@ -2885,22 +1937,6 @@
     if ( allDepsAreNormalPtr != nullptr )
         *allDepsAreNormalPtr = allDepsAreNormal;
     return count;
-}
-
-bool MachOFile::hasPlusLoadMethod(Diagnostics& diag) const
-{
-    __block bool result = false;
-
-    // in new objc runtime compiler puts classes/categories with +load method in specical section
-    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
-        if ( strncmp(info.segInfo.segName, "__DATA", 6) != 0 )
-            return;
-        if ( (strcmp(info.sectName, "__objc_nlclslist") == 0) || (strcmp(info.sectName, "__objc_nlcatlist") == 0)) {
-            result = true;
-            stop = true;
-        }
-    });
-    return result;
 }
 
 uint32_t MachOFile::getFixupsLoadCommandFileOffset() const
@@ -2948,8 +1984,8 @@
     if ( result )
         return true;
 
-    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
-        if ( (info.sectFlags & SECTION_TYPE) != S_INIT_FUNC_OFFSETS )
+    forEachSection(^(const Header::SectionInfo& info, bool& stop) {
+        if ( (info.flags & SECTION_TYPE) != S_INIT_FUNC_OFFSETS )
             return;
         result = true;
         stop = true;
@@ -2961,67 +1997,26 @@
 void MachOFile::forEachInitializerPointerSection(Diagnostics& diag, void (^callback)(uint32_t sectionOffset, uint32_t sectionSize, bool& stop)) const
 {
     const unsigned ptrSize     = pointerSize();
-    const uint64_t baseAddress = preferredLoadAddress();
-    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& sectStop) {
-        if ( (info.sectFlags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
-            if ( (info.sectSize % ptrSize) != 0 ) {
-                diag.error("initializer section %s/%s has bad size", info.segInfo.segName, info.sectName);
+    const uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
+    forEachSection(^(const Header::SectionInfo& info, bool& sectStop) {
+        if ( (info.flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
+            if ( (info.size % ptrSize) != 0 ) {
+                diag.error("initializer section %.*s/%.*s has bad size",
+                           (int)info.segmentName.size(), info.segmentName.data(),
+                           (int)info.sectionName.size(), info.sectionName.data());
                 sectStop = true;
                 return;
             }
-            if ( malformedSectionRange ) {
-                diag.error("initializer section %s/%s extends beyond its segment", info.segInfo.segName, info.sectName);
+            if ( (info.address % ptrSize) != 0 ) {
+                diag.error("initializer section %.*s/%.*s is not pointer aligned",
+                           (int)info.segmentName.size(), info.segmentName.data(),
+                           (int)info.sectionName.size(), info.sectionName.data());
                 sectStop = true;
                 return;
             }
-            if ( (info.sectAddr % ptrSize) != 0 ) {
-                diag.error("initializer section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
-                sectStop = true;
-                return;
-            }
-            callback((uint32_t)(info.sectAddr - baseAddress), (uint32_t)info.sectSize, sectStop);
+            callback((uint32_t)(info.address - baseAddress), (uint32_t)info.size, sectStop);
         }
     });
-}
-
-bool MachOFile::hasCodeSignature() const
-{
-    return this->hasLoadCommand(LC_CODE_SIGNATURE);
-}
-
-bool MachOFile::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
-{
-    fileOffset = 0;
-    size = 0;
-
-    Diagnostics diag;
-    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_CODE_SIGNATURE ) {
-            const linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
-            fileOffset = sigCmd->dataoff;
-            size       = sigCmd->datasize;
-            stop = true;
-        }
-    });
-    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
-
-    // early exist if no LC_CODE_SIGNATURE
-    if ( fileOffset == 0 )
-        return false;
-
-    // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
-    if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) ) {
-        __block bool foundPlatform = false;
-        __block bool badSignature  = false;
-        forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
-            foundPlatform = true;
-            if ( (platform == Platform::macOS) && (sdk < 0x000A0900) )
-                badSignature = true;
-        });
-        return foundPlatform && !badSignature;
-    }
-
-    return true;
 }
 
 uint64_t MachOFile::mappedSize() const
@@ -3038,20 +2033,20 @@
     __block uint64_t lowestVmAddr   = 0xFFFFFFFFFFFFFFFFULL;
     __block uint64_t highestVmAddr  = 0;
     __block uint64_t sumVmSizes     = 0;
-    forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
-        if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 )
+    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& segmentInfo, bool& stop) {
+        if ( segmentInfo.segmentName == "__PAGEZERO" )
             return;
-        if ( segmentInfo.writable() && (segmentInfo.fileSize !=  segmentInfo.vmSize) )
+        if ( segmentInfo.writable() && (segmentInfo.fileSize !=  segmentInfo.vmsize) )
             writeExpansion = true; // zerofill at end of __DATA
-        if ( segmentInfo.vmSize == 0 ) {
+        if ( segmentInfo.vmsize == 0 ) {
             // Always zero fill if we have zero-sized segments
             writeExpansion = true;
         }
-        if ( segmentInfo.vmAddr < lowestVmAddr )
-            lowestVmAddr = segmentInfo.vmAddr;
-        if ( segmentInfo.vmAddr+segmentInfo.vmSize > highestVmAddr )
-            highestVmAddr = segmentInfo.vmAddr+segmentInfo.vmSize;
-        sumVmSizes += segmentInfo.vmSize;
+        if ( segmentInfo.vmaddr < lowestVmAddr )
+            lowestVmAddr = segmentInfo.vmaddr;
+        if ( segmentInfo.vmaddr+segmentInfo.vmsize > highestVmAddr )
+            highestVmAddr = segmentInfo.vmaddr + segmentInfo.vmsize;
+        sumVmSizes += segmentInfo.vmsize;
     });
     uint64_t totalVmSpace = (highestVmAddr - lowestVmAddr);
     // LINKEDIT vmSize is not required to be a multiple of page size.  Round up if that is the case
@@ -3061,8 +2056,8 @@
 
     // The aux KC may have __DATA first, in which case we always want to vm_copy to the right place
     bool hasOutOfOrderSegments = false;
-#if BUILDING_APP_CACHE_UTIL
-    uint64_t textSegVMAddr = preferredLoadAddress();
+#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
+    uint64_t textSegVMAddr = ((const Header*)this)->preferredLoadAddress();
     hasOutOfOrderSegments = textSegVMAddr != lowestVmAddr;
 #endif
 
@@ -3070,21 +2065,11 @@
     hasZeroFill = writeExpansion || hasHole || hasOutOfOrderSegments;
 }
 
-uint32_t MachOFile::segmentCount() const
-{
-    __block uint32_t count   = 0;
-    forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        ++count;
-    });
-    return count;
-}
-
-
 void MachOFile::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const
 {
-    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
-        if ( ( (info.sectFlags & SECTION_TYPE) == S_DTRACE_DOF ) && !malformedSectionRange ) {
-            callback((uint32_t)(info.sectAddr - info.segInfo.vmAddr));
+    forEachSection(^(const Header::SegmentInfo& segInfo, const Header::SectionInfo& info, bool &stop) {
+        if ( (info.flags & SECTION_TYPE) == S_DTRACE_DOF ) {
+            callback((uint32_t)(info.address - segInfo.vmaddr));
         }
     });
 }
@@ -3094,11 +2079,11 @@
     __block uint64_t textUnslidVMAddr   = 0;
     __block uint64_t linkeditUnslidVMAddr   = 0;
     __block uint64_t linkeditFileOffset     = 0;
-    forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        if ( strcmp(info.segName, "__TEXT") == 0 ) {
-            textUnslidVMAddr = info.vmAddr;
-        } else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
-            linkeditUnslidVMAddr = info.vmAddr;
+    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
+        if ( info.segmentName == "__TEXT" ) {
+            textUnslidVMAddr = info.vmaddr;
+        } else if ( info.segmentName == "__LINKEDIT" ) {
+            linkeditUnslidVMAddr = info.vmaddr;
             linkeditFileOffset   = info.fileOffset;
             stop = true;
         }
@@ -3203,7 +2188,7 @@
 
     // Note: The kernel sometimes chooses sha1 on watchOS, and sometimes sha256.
     // Embed all of them so that we just need to match any of them
-    const bool isWatchOS = this->builtForPlatform(Platform::watchOS);
+    const bool isWatchOS = ((const Header*)this)->builtForPlatform(Platform::watchOS);
     const bool isMainExecutable = this->isMainExecutable();
     auto hashRankFn = isWatchOS ? &hash_rank_watchOS_dylibs : &hash_rank;
 
@@ -3320,6 +2305,7 @@
     this->authenticated       = 0;
     this->key                 = 0;
     this->usesAddrDiversity   = 0;
+    this->padding             = 0;
 }
 
 MachOFile::PointerMetaData::PointerMetaData(const ChainedFixupPointerOnDisk* fixupLoc, uint16_t pointer_format)
@@ -3329,6 +2315,7 @@
     this->authenticated       = 0;
     this->key                 = 0;
     this->usesAddrDiversity   = 0;
+    this->padding             = 0;
     switch ( pointer_format ) {
         case DYLD_CHAINED_PTR_ARM64E:
         case DYLD_CHAINED_PTR_ARM64E_KERNEL:
@@ -3345,6 +2332,17 @@
                 this->high8             = fixupLoc->arm64e.rebase.high8;
             }
             break;
+        case DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE:
+            this->authenticated = fixupLoc->cache64e.auth.auth;
+            if ( this->authenticated ) {
+                this->key               = fixupLoc->cache64e.auth.keyIsData ? 2 : 0; // true -> DA (2), false -> IA (0)
+                this->usesAddrDiversity = fixupLoc->cache64e.auth.addrDiv;
+                this->diversity         = fixupLoc->cache64e.auth.diversity;
+            }
+            else {
+                this->high8 = fixupLoc->cache64e.regular.high8;
+            }
+            break;
         case DYLD_CHAINED_PTR_64:
         case DYLD_CHAINED_PTR_64_OFFSET:
             if ( fixupLoc->generic64.bind.bind == 0 )
@@ -3362,7 +2360,7 @@
         && (this->usesAddrDiversity == other.usesAddrDiversity);
 }
 
-#if !SUPPORT_VM_LAYOUT
+#if !SUPPORT_VM_LAYOUT || BUILDING_UNIT_TESTS || BUILDING_DYLD_SYMBOLS_CACHE
 bool MachOFile::getLinkeditLayout(Diagnostics& diag, mach_o::LinkeditLayout& layout) const
 {
     // Note, in file layout all linkedit offsets are just file offsets.
@@ -3561,25 +2559,25 @@
         return;
     }
 
-    uint32_t numSegments = this->segmentCount();
+    uint32_t numSegments = ((const Header*)this)->segmentCount();
     BLOCK_ACCCESSIBLE_ARRAY(mach_o::SegmentLayout, segmentLayout, numSegments);
-    this->forEachSegment(^(const SegmentInfo &info, bool &stop) {
+    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
         mach_o::SegmentLayout segment;
-        segment.vmAddr      = info.vmAddr;
-        segment.vmSize      = info.vmSize;
+        segment.vmAddr      = info.vmaddr;
+        segment.vmSize      = info.vmsize;
         segment.fileOffset  = info.fileOffset;
         segment.fileSize    = info.fileSize;
         segment.buffer      = (uint8_t*)this + info.fileOffset;
-        segment.protections = info.protections;
+        segment.protections = info.initProt;
 
         segment.kind        = mach_o::SegmentLayout::Kind::unknown;
-        if ( !strcmp(info.segName, "__TEXT") ) {
+        if ( info.segmentName == "__TEXT" ) {
             segment.kind    = mach_o::SegmentLayout::Kind::text;
-        } else if ( !strcmp(info.segName, "__LINKEDIT") ) {
+        } else if ( info.segmentName == "__LINKEDIT" ) {
             segment.kind    = mach_o::SegmentLayout::Kind::linkedit;
         }
 
-        segmentLayout[info.segIndex] = segment;
+        segmentLayout[info.segmentIndex] = segment;
     });
 
     mach_o::Layout layout(this, { &segmentLayout[0], &segmentLayout[numSegments] }, linkedit);
@@ -3587,59 +2585,10 @@
 }
 #endif // !SUPPORT_VM_LAYOUT
 
-bool MachOFile::hasObjCMessageReferences() const {
-
-    __block bool foundSection = false;
-    forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
-        if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
-            return;
-        if ( strcmp(sectInfo.sectName, "__objc_msgrefs") != 0 )
-            return;
-        foundSection = true;
-        stop = true;
-    });
-    return foundSection;
-}
-
-uint32_t MachOFile::loadCommandsFreeSpace() const
-{
-    __block uint32_t firstSectionFileOffset = 0;
-    __block uint32_t firstSegmentFileOffset = 0;
-    forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
-        firstSectionFileOffset = sectInfo.sectFileOffset;
-        firstSegmentFileOffset = (uint32_t)sectInfo.segInfo.fileOffset;
-        stop = true;
-    });
-
-    uint32_t headerSize = (this->magic == MH_MAGIC_64) ? sizeof(mach_header_64) : sizeof(mach_header);
-    uint32_t existSpaceUsed = this->sizeofcmds + headerSize;
-    return firstSectionFileOffset - firstSegmentFileOffset - existSpaceUsed;
-}
-
-bool MachOFile::findObjCDataSection(const char *sectionName, uint64_t& sectionRuntimeOffset, uint64_t& sectionSize) const
-{
-    uint64_t baseAddress = preferredLoadAddress();
-
-    __block bool foundSection = false;
-    forEachSection(^(const 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) )
-            return;
-        if ( strcmp(sectInfo.sectName, sectionName) != 0 )
-            return;
-        foundSection         = true;
-        sectionRuntimeOffset = sectInfo.sectAddr - baseAddress;
-        sectionSize          = sectInfo.sectSize;
-        stop                 = true;
-    });
-    return foundSection;
-}
-
 bool MachOFile::enforceFormat(Malformed kind) const
 {
     // TODO: Add a mapping from generic releases to platform versions
-#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
+#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
     // HACK: If we are the kernel, we have a different format to enforce
     if ( isFileSet() ) {
         bool result = false;
@@ -3713,127 +2662,131 @@
 #endif
 
     __block bool result = false;
-    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
-        switch (platform) {
-        case Platform::macOS:
+    mach_o::PlatformAndVersions pvs = ((const Header*)this)->platformAndVersions();
+    pvs.unzip(^(mach_o::PlatformAndVersions p) {
+        if ( p.platform == Platform::macOS ) {
             switch (kind) {
-            case Malformed::linkeditOrder:
-            case Malformed::linkeditAlignment:
-            case Malformed::dyldInfoAndlocalRelocs:
-                // enforce these checks on new binaries only
-                if (sdk >= 0x000A0E00) // macOS 10.14
+                case Malformed::linkeditOrder:
+                case Malformed::linkeditAlignment:
+                case Malformed::dyldInfoAndlocalRelocs:
+                    // enforce these checks on new binaries only
+                    if (p.sdk.value() >= 0x000A0E00) // macOS 10.14
+                        result = true;
+                    break;
+                case Malformed::segmentOrder:
+                case Malformed::linkeditPermissions:
+                case Malformed::textPermissions:
+                case Malformed::executableData:
+                case Malformed::writableData:
+                case Malformed::codeSigAlignment:
+                    // enforce these checks on new binaries only
+                    if (p.sdk.value() >= 0x000A0F00) // macOS 10.15
+                        result = true;
+                    break;
+                case Malformed::sectionsAddrRangeWithinSegment:
+                    // enforce these checks on new binaries only
+                    if (p.sdk.value() >= 0x000A1000) // macOS 10.16
+                        result = true;
+                    break;
+                case Malformed::noLinkedDylibs:
+                case Malformed::loaderPathsAreReal:
+                case Malformed::mainExecInDyldCache:
+                case Malformed::zerofillSwiftMetadata:
+                case Malformed::sdkOnOrAfter2021:
+                    // enforce these checks on new binaries only
+                    if (p.sdk.value() >= 0x000D0000) // macOS 13.0
+                        result = true;
+                    break;
+                case Malformed::noUUID:
+                case Malformed::sdkOnOrAfter2022:
+                    if (p.sdk.value() >= 0x000E0000) // macOS 14.0  FIXME
+                        result = true;
+                    break;
+            }
+        } else if ( p.platform == Platform::iOS || p.platform == Platform::tvOS || p.platform == Platform::macCatalyst ) {
+            switch (kind) {
+                case Malformed::linkeditOrder:
+                case Malformed::dyldInfoAndlocalRelocs:
+                case Malformed::textPermissions:
+                case Malformed::executableData:
+                case Malformed::writableData:
                     result = true;
-                break;
-            case Malformed::segmentOrder:
-            case Malformed::linkeditPermissions:
-            case Malformed::textPermissions:
-            case Malformed::executableData:
-            case Malformed::writableData:
-            case Malformed::codeSigAlignment:
-                // enforce these checks on new binaries only
-                if (sdk >= 0x000A0F00) // macOS 10.15
+                    break;
+                case Malformed::linkeditAlignment:
+                case Malformed::segmentOrder:
+                case Malformed::linkeditPermissions:
+                case Malformed::codeSigAlignment:
+                    // enforce these checks on new binaries only
+                    if (p.sdk.value() >= 0x000D0000) // iOS 13
+                        result = true;
+                    break;
+                case Malformed::sectionsAddrRangeWithinSegment:
+                    // enforce these checks on new binaries only
+                    if (p.sdk.value() >= 0x000E0000) // iOS 14
+                        result = true;
+                    break;
+                case Malformed::noLinkedDylibs:
+                case Malformed::loaderPathsAreReal:
+                case Malformed::mainExecInDyldCache:
+                case Malformed::zerofillSwiftMetadata:
+                case Malformed::sdkOnOrAfter2021:
+                    // enforce these checks on new binaries only
+                    if (p.sdk.value() >= 0x00100000) // iOS 16
+                        result = true;
+                    break;
+                case Malformed::noUUID:
+                case Malformed::sdkOnOrAfter2022:
+                    if (p.sdk.value() >= 0x00110000) // iOS 17.0 FIXME
+                        result = true;
+                    break;
+            }
+        } else if ( p.platform == Platform::watchOS ) {
+            switch (kind) {
+                case Malformed::linkeditOrder:
+                case Malformed::dyldInfoAndlocalRelocs:
+                case Malformed::textPermissions:
+                case Malformed::executableData:
+                case Malformed::writableData:
                     result = true;
-                break;
-            case Malformed::sectionsAddrRangeWithinSegment:
-                // enforce these checks on new binaries only
-                if (sdk >= 0x000A1000) // macOS 10.16
-                    result = true;
-                break;
-            case Malformed::noLinkedDylibs:
-            case Malformed::loaderPathsAreReal:
-            case Malformed::mainExecInDyldCache:
-            case Malformed::zerofillSwiftMetadata:
-            case Malformed::sdkOnOrAfter2021:
-                // enforce these checks on new binaries only
-                if (sdk >= 0x000D0000) // macOS 13.0
-                    result = true;
-                break;
-            case Malformed::noUUID:
-            case Malformed::sdkOnOrAfter2022:
-                if (sdk >= 0x000E0000) // macOS 14.0  FIXME
-                    result = true;
-                break;
-            }
-            break;
-        case Platform::iOS:
-        case Platform::tvOS:
-        case Platform::iOSMac:
-            switch (kind) {
-            case Malformed::linkeditOrder:
-            case Malformed::dyldInfoAndlocalRelocs:
-            case Malformed::textPermissions:
-            case Malformed::executableData:
-            case Malformed::writableData:
-                result = true;
-                break;
-            case Malformed::linkeditAlignment:
-            case Malformed::segmentOrder:
-            case Malformed::linkeditPermissions:
-            case Malformed::codeSigAlignment:
-                // enforce these checks on new binaries only
-                if (sdk >= 0x000D0000) // iOS 13
-                    result = true;
-                break;
-            case Malformed::sectionsAddrRangeWithinSegment:
-                // enforce these checks on new binaries only
-                if (sdk >= 0x000E0000) // iOS 14
-                    result = true;
-                break;
-            case Malformed::noLinkedDylibs:
-            case Malformed::loaderPathsAreReal:
-            case Malformed::mainExecInDyldCache:
-            case Malformed::zerofillSwiftMetadata:
-            case Malformed::sdkOnOrAfter2021:
-                // enforce these checks on new binaries only
-                if (sdk >= 0x00100000) // iOS 16
-                    result = true;
-                break;
-            case Malformed::noUUID:
-            case Malformed::sdkOnOrAfter2022:
-                if (sdk >= 0x00110000) // iOS 17.0 FIXME
-                    result = true;
-                break;
-            }
-            break;
-        case Platform::watchOS:
-            switch (kind) {
-            case Malformed::linkeditOrder:
-            case Malformed::dyldInfoAndlocalRelocs:
-            case Malformed::textPermissions:
-            case Malformed::executableData:
-            case Malformed::writableData:
-                result = true;
-                break;
-            case Malformed::linkeditAlignment:
-            case Malformed::segmentOrder:
-            case Malformed::linkeditPermissions:
-            case Malformed::codeSigAlignment:
-            case Malformed::sectionsAddrRangeWithinSegment:
-            case Malformed::noLinkedDylibs:
-            case Malformed::loaderPathsAreReal:
-            case Malformed::mainExecInDyldCache:
-            case Malformed::zerofillSwiftMetadata:
-            case Malformed::sdkOnOrAfter2021:
-                // enforce these checks on new binaries only
-                if (sdk >= 0x00090000) // watchOS 9
-                    result = true;
-                break;
-            case Malformed::noUUID:
-            case Malformed::sdkOnOrAfter2022:
-                if (sdk >= 0x000A0000) // watchOS 10 FIXME
-                    result = true;
-                break;
-            }
-            break;
-        case Platform::driverKit:
+                    break;
+                case Malformed::linkeditAlignment:
+                case Malformed::segmentOrder:
+                case Malformed::linkeditPermissions:
+                case Malformed::codeSigAlignment:
+                case Malformed::sectionsAddrRangeWithinSegment:
+                case Malformed::noLinkedDylibs:
+                case Malformed::loaderPathsAreReal:
+                case Malformed::mainExecInDyldCache:
+                case Malformed::zerofillSwiftMetadata:
+                case Malformed::sdkOnOrAfter2021:
+                    // enforce these checks on new binaries only
+                    if (p.sdk.value() >= 0x00090000) // watchOS 9
+                        result = true;
+                    break;
+                case Malformed::noUUID:
+                case Malformed::sdkOnOrAfter2022:
+                    if (p.sdk.value() >= 0x000A0000) // watchOS 10 FIXME
+                        result = true;
+                    break;
+            }
+        } else if ( p.platform == Platform::driverKit ) {
             result = true;
-            break;
-        default:
+        } else if ( p.platform == Platform::visionOS || p.platform == Platform::visionOS_simulator ) {
+            result = true; // do all checks by default
+            if ( kind == Malformed::sdkOnOrAfter2022 ) {
+                if (p.sdk.value() < 0x00020000) // visionOS 2.0 FIXME
+                    result = false;
+            }
+        }
+        
+        // if binary is so old, there is no platform info, don't enforce malformed errors
+        else if ( p.platform.empty() ) {
+            result = false;
+        } else {
             result = true;
-            break;
         }
     });
-    // if binary is so old, there is no platform info, don't enforce malformed errors
+    
     return result;
 }
 
@@ -3906,43 +2859,47 @@
     __block bool badSize        = false;
     __block bool hasTEXT        = false;
     __block bool hasLINKEDIT    = false;
-    forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        if ( strcmp(info.segName, "__TEXT") == 0 ) {
-            if ( (info.protections != (VM_PROT_READ|VM_PROT_EXECUTE)) && enforceFormat(Malformed::textPermissions) ) {
+    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
+        if ( info.segmentName == "__TEXT" ) {
+            if ( (info.initProt != (VM_PROT_READ|VM_PROT_EXECUTE)) && enforceFormat(Malformed::textPermissions) ) {
                 diag.error("in '%s' __TEXT segment permissions is not 'r-x'", path);
                 badPermissions = true;
                 stop = true;
             }
             hasTEXT = true;
         }
-        else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
-            if ( (info.protections != VM_PROT_READ) && enforceFormat(Malformed::linkeditPermissions) ) {
+        else if ( info.segmentName == "__LINKEDIT" ) {
+            if ( (info.initProt != VM_PROT_READ) && enforceFormat(Malformed::linkeditPermissions) ) {
                 diag.error("in '%s' __LINKEDIT segment permissions is not 'r--'", path);
                 badPermissions = true;
                 stop = true;
             }
             hasLINKEDIT = true;
         }
-        else if ( (info.protections & 0xFFFFFFF8) != 0 ) {
-            diag.error("in '%s' %s segment permissions has invalid bits set", path, info.segName);
+        else if ( (info.initProt & 0xFFFFFFF8) != 0 ) {
+            diag.error("in '%s' %.*s segment permissions has invalid bits set", path,
+                       (int)info.segmentName.size(), info.segmentName.data());
             badPermissions = true;
             stop = true;
         }
         if ( greaterThanAddOrOverflow(info.fileOffset, info.fileSize, fileLen) ) {
-            diag.error("in '%s' %s segment content extends beyond end of file", path, info.segName);
+            diag.error("in '%s' %.*s segment content extends beyond end of file", path,
+                       (int)info.segmentName.size(), info.segmentName.data());
             badSize = true;
             stop = true;
         }
         if ( is64() ) {
-            if ( info.vmAddr+info.vmSize < info.vmAddr ) {
-                diag.error("in '%s' %s segment vm range wraps", path, info.segName);
+            if ( info.vmaddr + info.vmsize < info.vmaddr ) {
+                diag.error("in '%s' %.*s segment vm range wraps", path,
+                           (int)info.segmentName.size(), info.segmentName.data());
                 badSize = true;
                 stop = true;
             }
        }
        else {
-            if ( (uint32_t)(info.vmAddr+info.vmSize) < (uint32_t)(info.vmAddr) ) {
-                diag.error("in '%s' %s segment vm range wraps", path, info.segName);
+            if ( (uint32_t)(info.vmaddr + info.vmsize) < (uint32_t)(info.vmaddr) ) {
+                diag.error("in '%s' %.*s segment vm range wraps", path,
+                           (int)info.segmentName.size(), info.segmentName.data());
                 badSize = true;
                 stop = true;
             }
@@ -3961,16 +2918,18 @@
 
     // check for overlapping segments
     __block bool badSegments = false;
-    forEachSegment(^(const SegmentInfo& info1, bool& stop1) {
-        uint64_t seg1vmEnd   = info1.vmAddr + info1.vmSize;
+    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info1, bool& stop1) {
+        uint64_t seg1vmEnd   = info1.vmaddr + info1.vmsize;
         uint64_t seg1FileEnd = info1.fileOffset + info1.fileSize;
-        forEachSegment(^(const SegmentInfo& info2, bool& stop2) {
-            if ( info1.segIndex == info2.segIndex )
+        ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info2, bool& stop2) {
+            if ( info1.segmentIndex == info2.segmentIndex )
                 return;
-            uint64_t seg2vmEnd   = info2.vmAddr + info2.vmSize;
+            uint64_t seg2vmEnd   = info2.vmaddr + info2.vmsize;
             uint64_t seg2FileEnd = info2.fileOffset + info2.fileSize;
-            if ( ((info2.vmAddr <= info1.vmAddr) && (seg2vmEnd > info1.vmAddr) && (seg1vmEnd > info1.vmAddr )) || ((info2.vmAddr >= info1.vmAddr ) && (info2.vmAddr < seg1vmEnd) && (seg2vmEnd > info2.vmAddr)) ) {
-                diag.error("in '%s' segment %s vm range overlaps segment %s", path, info1.segName, info2.segName);
+            if ( ((info2.vmaddr <= info1.vmaddr) && (seg2vmEnd > info1.vmaddr) && (seg1vmEnd > info1.vmaddr )) || ((info2.vmaddr >= info1.vmaddr ) && (info2.vmaddr < seg1vmEnd) && (seg2vmEnd > info2.vmaddr)) ) {
+                diag.error("in '%s' segment %.*s vm range overlaps segment %.*s", path,
+                           (int)info1.segmentName.size(), info1.segmentName.data(),
+                           (int)info2.segmentName.size(), info2.segmentName.data());
                 badSegments = true;
                 stop1 = true;
                 stop2 = true;
@@ -3980,20 +2939,24 @@
                      // HACK: Split shared caches might put the __TEXT in a SubCache, then the __DATA in a later SubCache.
                      // The file offsets are in to each SubCache file, which means that they might overlap
                      // For now we have no choice but to disable this error
-                     diag.error("in '%s' segment %s file content overlaps segment %s", path, info1.segName, info2.segName);
+                     diag.error("in '%s' segment %.*s file content overlaps segment %.*s", path,
+                                (int)info1.segmentName.size(), info1.segmentName.data(),
+                                (int)info2.segmentName.size(), info2.segmentName.data());
                      badSegments = true;
                      stop1 = true;
                      stop2 = true;
                  }
             }
-            if ( (info1.segIndex < info2.segIndex) && !stop1 ) {
-                if ( (info1.vmAddr > info2.vmAddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset  != 0)) ){
+            if ( (info1.segmentIndex < info2.segmentIndex) && !stop1 ) {
+                if ( (info1.vmaddr > info2.vmaddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset  != 0)) ){
                     if ( !inDyldCache() && enforceFormat(Malformed::segmentOrder) && !isStaticExecutable() ) {
                         // <rdar://80084852> whitelist go libraries __DWARF segments
-                        if ( (strcmp(info1.segName, "__DWARF") != 0 && strcmp(info2.segName, "__DWARF") != 0) ) {
+                        if ( info1.segmentName != "__DWARF" && info2.segmentName != "__DWARF" ) {
                             // dyld cache __DATA_* segments are moved around
                             // The static kernel also has segments with vmAddr's before __TEXT
-                            diag.error("in '%s' segment load commands out of order with respect to layout for %s and %s", path, info1.segName, info2.segName);
+                            diag.error("in '%s' segment load commands out of order with respect to layout for %.*s and %.*s", path,
+                                       (int)info1.segmentName.size(), info1.segmentName.data(),
+                                       (int)info2.segmentName.size(), info2.segmentName.data());
                             badSegments = true;
                             stop1 = true;
                             stop2 = true;
@@ -4024,7 +2987,7 @@
                 }
                 else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
                     bool ignoreError = !enforceFormat(Malformed::sectionsAddrRangeWithinSegment);
-#if BUILDING_APP_CACHE_UTIL
+#if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
                     if ( (seg->vmsize == 0) && !strcmp(seg->segname, "__CTF") )
                         ignoreError = true;
 #endif
@@ -4059,37 +3022,5 @@
     return !badSections;
 }
 
-void MachOFile::forEachSingletonPatch(Diagnostics& diag, void (^handler)(SingletonPatchKind kind,
-                                                                         uint64_t runtimeOffset)) const
-{
-    uint32_t ptrSize = this->pointerSize();
-    uint32_t elementSize = (2 * ptrSize);
-    uint64_t loadAddress = this->preferredLoadAddress();
-    this->forEachSection(^(const SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
-        if ( strcmp(sectInfo.sectName, "__const_cfobj2") != 0 )
-            return;
-        stop = true;
-
-        if ( (sectInfo.sectSize % elementSize) != 0 ) {
-            diag.error("Incorrect patching size (%lld).  Should be a multiple of (2 * ptrSize)", sectInfo.sectSize);
-            return;
-        }
-
-        if ( sectInfo.reserved2 != elementSize ) {
-            // ld64 must have rejected one or more of the elements in the section, so
-            // didn't set the reserved2 to let us patch
-            diag.error("reserved2 is unsupported value %d.  Expected %d",
-                       sectInfo.reserved2, elementSize);
-            return;
-        }
-
-        for ( uint64_t offset = 0; offset != sectInfo.sectSize; offset += elementSize ) {
-            uint64_t targetRuntimeOffset = (sectInfo.sectAddr + offset) - loadAddress;
-            handler(SingletonPatchKind::cfObj2, targetRuntimeOffset);
-        }
-    });
-}
-
-
 } // namespace dyld3