Loading...
dyld3/MachOFile.cpp dyld-750.5 dyld-851.27
--- dyld/dyld-750.5/dyld3/MachOFile.cpp
+++ dyld/dyld-851.27/dyld3/MachOFile.cpp
@@ -25,6 +25,11 @@
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
 #include <TargetConditionals.h>
 #include <mach/host_info.h>
 #include <mach/mach.h>
@@ -34,8 +39,49 @@
 #include "MachOFile.h"
 #include "SupportedArchs.h"
 
+#if BUILDING_DYLD || BUILDING_LIBDYLD
+    // define away restrict until rdar://60166935 is fixed
+    #define restrict
+    #include <subsystem.h>
+#endif
 
 namespace dyld3 {
+
+////////////////////////////  posix wrappers ////////////////////////////////////////
+
+// <rdar://problem/10111032> wrap calls to stat() with check for EAGAIN
+int stat(const char* path, struct stat* buf)
+{
+    int result;
+    do {
+#if BUILDING_DYLD || BUILDING_LIBDYLD
+        result = ::stat_with_subsystem(path, buf);
+#else
+        result = ::stat(path, buf);
+#endif
+    } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
+
+    return result;
+}
+
+// <rdar://problem/13805025> dyld should retry open() if it gets an EGAIN
+int open(const char* path, int flag, int other)
+{
+    int result;
+    do {
+#if BUILDING_DYLD || BUILDING_LIBDYLD
+        if (flag & O_CREAT)
+            result = ::open(path, flag, other);
+        else
+            result = ::open_with_subsystem(path, flag);
+#else
+        result = ::open(path, flag, other);
+#endif
+    } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
+
+    return result;
+}
+
 
 ////////////////////////////  FatFile ////////////////////////////////////////
 
@@ -130,7 +176,8 @@
     }
 }
 
-bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
+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)) )
@@ -138,7 +185,7 @@
 
     __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)) {
+        if (int sliceGrade = archs.grade(sliceCpuType, sliceCpuSubType, isOSBinary)) {
             if ( sliceGrade > bestGrade ) {
                 sliceOffset = (char*)sliceStart - (char*)this;
                 sliceLen    = sliceSize;
@@ -158,26 +205,47 @@
 
 ////////////////////////////  GradedArchs ////////////////////////////////////////
 
-const GradedArchs GradedArchs::i386          = { {{CPU_TYPE_I386,     CPU_SUBTYPE_I386_ALL,    1}} };
-const GradedArchs GradedArchs::x86_64        = { {{CPU_TYPE_X86_64,   CPU_SUBTYPE_X86_64_ALL,  1}} };
-const GradedArchs GradedArchs::x86_64h       = { {{CPU_TYPE_X86_64,   CPU_SUBTYPE_X86_64_H,    2}, {CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, 1}},     };
-const GradedArchs GradedArchs::arm64         = { {{CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64_ALL,   1}} };
+
+#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_ARM64_ALL,   false
+#define GRADE_armv7s      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7,      false
+#define GRADE_armv7k      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7K,     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              = { {{GRADE_i386,    1}} };
+const GradedArchs GradedArchs::x86_64            = { {{GRADE_x86_64,  1}} };
+const GradedArchs GradedArchs::x86_64h           = { {{GRADE_x86_64h, 2}, {GRADE_x86_64, 1}} };
+const GradedArchs GradedArchs::arm64             = { {{GRADE_arm64,   1}} };
 #if SUPPORT_ARCH_arm64e
-const GradedArchs GradedArchs::arm64e_compat = { {{CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64E,      2}, {CPU_TYPE_ARM64,  CPU_SUBTYPE_ARM64_ALL, 1}} };
-const GradedArchs GradedArchs::arm64e        = { {{CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64E,      1}} };
-#endif
-const GradedArchs GradedArchs::armv7k        = { {{CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7K,     1}} };
-const GradedArchs GradedArchs::armv7         = { {{CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7,      1}} };
-const GradedArchs GradedArchs::armv7s        = { {{CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7S,     2}, {CPU_TYPE_ARM,    CPU_SUBTYPE_ARM_V7, 1}} };
+const GradedArchs GradedArchs::arm64e_keysoff    = { {{GRADE_arm64e,    2}, {GRADE_arm64, 1}} };
+const GradedArchs GradedArchs::arm64e_keysoff_pb = { {{GRADE_arm64e_pb, 2}, {GRADE_arm64, 1}} };
+const GradedArchs GradedArchs::arm64e            = { {{GRADE_arm64e,    1}} };
+const GradedArchs GradedArchs::arm64e_pb         = { {{GRADE_arm64e_pb, 1}} };
+#endif
+const GradedArchs GradedArchs::armv7             = { {{GRADE_armv7,   1}} };
+const GradedArchs GradedArchs::armv7s            = { {{GRADE_armv7s,  2}, {GRADE_armv7, 1}} };
+const GradedArchs GradedArchs::armv7k            = { {{GRADE_armv7k,  1}} };
 #if SUPPORT_ARCH_arm64_32
-const GradedArchs GradedArchs::arm64_32      = { {{CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8, 1}} };
-#endif
-
-int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype) const
+const GradedArchs GradedArchs::arm64_32          = { {{GRADE_arm64_32, 1}} };
+#endif
+
+int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype, bool isOSBinary) const
 {
     for (const CpuGrade* p = _orderedCpuTypes; p->type != 0; ++p) {
-        if ( (p->type == cputype) && (p->subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) )
-            return p->grade;
+        if ( (p->type == cputype) && (p->subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) ) {
+            if ( p->osBinary ) {
+                if ( isOSBinary )
+                    return p->grade;
+            }
+            else {
+                return p->grade;
+            }
+        }
     }
     return 0;
 }
@@ -206,13 +274,13 @@
 }
 #endif
 
-const GradedArchs& GradedArchs::forCurrentOS(const MachOFile* mainExecutable)
+const GradedArchs& GradedArchs::forCurrentOS(bool keysOff, bool osBinariesOnly)
 {
 #if __arm64e__
-    if ( mainExecutable->cpusubtype < CPU_SUBTYPE_ARM64E )
-        return arm64e_compat;
+    if ( osBinariesOnly )
+        return (keysOff ? arm64e_keysoff_pb : arm64e_pb);
     else
-        return arm64e;
+        return (keysOff ? arm64e_keysoff : arm64e);
 #elif __ARM64_ARCH_8_32__
     return arm64_32;
 #elif __arm64__
@@ -232,7 +300,7 @@
 #endif
 }
 
-const GradedArchs& GradedArchs::forName(const char* archName, bool forMainExecutable)
+const GradedArchs& GradedArchs::forName(const char* archName, bool keysOff)
 {
     if (strcmp(archName, "x86_64h") == 0 )
         return x86_64h;
@@ -240,7 +308,7 @@
         return x86_64;
 #if SUPPORT_ARCH_arm64e
     else if (strcmp(archName, "arm64e") == 0 )
-        return forMainExecutable ? arm64e_compat : arm64e;
+        return keysOff ? arm64e_keysoff : arm64e;
 #endif
     else if (strcmp(archName, "arm64") == 0 )
         return arm64;
@@ -260,6 +328,7 @@
 }
 
 
+
 ////////////////////////////  MachOFile ////////////////////////////////////////
 
 
@@ -285,7 +354,7 @@
     { "tvOS",        Platform::tvOS,              LC_VERSION_MIN_TVOS     },
     { "watchOS",     Platform::watchOS,           LC_VERSION_MIN_WATCHOS  },
     { "bridgeOS",    Platform::bridgeOS,          LC_BUILD_VERSION        },
-    { "UIKitForMac", Platform::iOSMac,            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        },
@@ -303,6 +372,10 @@
     return is64() ? sizeof(mach_header_64) : sizeof(mach_header);
 }
 
+uint32_t MachOFile::maskedCpuSubtype() const
+{
+    return (this->cpusubtype & ~CPU_SUBTYPE_MASK);
+}
 
 uint32_t MachOFile::pointerSize() const
 {
@@ -404,26 +477,25 @@
     *s++ = '\0';
 }
 
-bool MachOFile::supportsPlatform(Platform reqPlatform) const
+bool MachOFile::builtForPlatform(Platform reqPlatform, bool onlyOnePlatform) const
 {
     __block bool foundRequestedPlatform = false;
-    __block bool foundOtherPlatform = 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;
-
-    // we did find some platform info, but not requested, so return false
-    if ( foundOtherPlatform )
-        return false;
 
     // binary has no explict load command to mark platform
     // could be an old macOS binary, look at arch
-    if  ( reqPlatform == Platform::macOS ) {
+    if  ( !foundOtherPlatform && (reqPlatform == Platform::macOS) ) {
         if ( this->cputype == CPU_TYPE_X86_64 )
             return true;
         if ( this->cputype == CPU_TYPE_I386 )
@@ -435,6 +507,52 @@
     if ( !foundOtherPlatform && (reqPlatform == Platform::unknown) )
         return true;
 #endif
+
+    return false;
+}
+
+bool MachOFile::loadableIntoProcess(Platform processPlatform, const char* path) 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* 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;
 }
@@ -475,8 +593,10 @@
     return Platform::tvOS;
 #elif TARGET_OS_IOS
     return Platform::iOS;
-#elif TARGET_OS_MAC
+#elif TARGET_OS_OSX
     return Platform::macOS;
+#elif TARGET_OS_DRIVERKIT
+    return Platform::driverKit;
 #else
     #error unknown platform
 #endif
@@ -546,6 +666,16 @@
 
     // static executables do not have dyld load command
     return !hasLoadCommand(LC_LOAD_DYLINKER);
+}
+
+bool MachOFile::isKextBundle() const
+{
+    return (this->filetype == MH_KEXT_BUNDLE);
+}
+
+bool MachOFile::isFileSet() const
+{
+    return (this->filetype == MH_FILESET);
 }
 
 bool MachOFile::isPIE() const
@@ -665,6 +795,52 @@
     }
 }
 
+void MachOFile::removeLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& remove, bool& stop))
+{
+    bool stop = false;
+    const load_command* startCmds = nullptr;
+    if ( this->magic == MH_MAGIC_64 )
+        startCmds = (load_command*)((char *)this + sizeof(mach_header_64));
+    else if ( this->magic == MH_MAGIC )
+        startCmds = (load_command*)((char *)this + sizeof(mach_header));
+    else if ( hasMachOBigEndianMagic() )
+        return;  // can't process big endian mach-o
+    else {
+        const uint32_t* h = (uint32_t*)this;
+        diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return;  // not a mach-o file
+    }
+    const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
+    auto cmd = (load_command*)startCmds;
+    const uint32_t origNcmds = this->ncmds;
+    unsigned bytesRemaining = this->sizeofcmds;
+    for (uint32_t i = 0; i < origNcmds; ++i) {
+        bool remove = false;
+        auto nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
+            return;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return;
+        }
+        callback(cmd, remove, stop);
+        if ( remove ) {
+            this->sizeofcmds -= cmd->cmdsize;
+            ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
+            this->ncmds--;
+        } else {
+            bytesRemaining -= cmd->cmdsize;
+            cmd = nextCmd;
+        }
+        if ( stop )
+            break;
+    }
+    if ( cmd )
+     ::bzero(cmd, bytesRemaining);
+}
+
 const char* MachOFile::installName() const
 {
     const char*  name;
@@ -786,6 +962,48 @@
         }
     });
     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;
+}
+
+
+uint32_t MachOFile::entryAddrRegisterIndexForThreadCmd() const
+{
+    switch ( this->cputype ) {
+        case CPU_TYPE_I386:
+            return 10; // i386_thread_state_t.eip
+        case CPU_TYPE_X86_64:
+            return 16; // x86_thread_state64_t.rip
+        case CPU_TYPE_ARM:
+            return 15; // arm_thread_state_t.pc
+        case CPU_TYPE_ARM64:
+            return 32; // arm_thread_state64_t.__pc
+    }
+    return ~0U;
+}
+
+
+uint64_t MachOFile::entryAddrFromThreadCmd(const thread_command* cmd) const
+{
+    assert(cmd->cmd == LC_UNIXTHREAD);
+    const uint32_t* regs32 = (uint32_t*)(((char*)cmd) + 16);
+    const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
+
+    uint32_t index = entryAddrRegisterIndexForThreadCmd();
+    if (index == ~0U)
+        return 0;
+
+    return is64() ? regs64[index] : regs32[index];
 }
 
 
@@ -987,8 +1205,94 @@
             || (strncmp(dylibName, "/Library/Apple/System/Library/", 30) == 0) );
 }
 
+static bool startsWith(const char* buffer, const char* valueToFind) {
+    return strncmp(buffer, valueToFind, strlen(valueToFind)) == 0;
+}
+
+static bool platformExcludesSharedCache_macOS(const char* installName) {
+    // Note: This function basically matches dontCache() from update dyld shared cache
+
+    if ( startsWith(installName, "/usr/lib/system/introspection/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/QuickTime/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/Tcl/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/Perl/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/MonitorPanels/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/Accessibility/") )
+        return true;
+    if ( startsWith(installName, "/usr/local/") )
+        return true;
+    if ( startsWith(installName, "/usr/lib/pam/") )
+        return true;
+    // We no longer support ROSP, so skip all paths which start with the special prefix
+    if ( startsWith(installName, "/System/Library/Templates/Data/") )
+        return true;
+
+    // anything inside a .app bundle is specific to app, so should not be in shared cache
+    if ( strstr(installName, ".app/") != NULL )
+        return true;
+
+    return false;
+}
+
+static bool platformExcludesSharedCache_iOS(const char* installName) {
+    if ( strcmp(installName, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
+        return true;
+    if ( strcmp(installName, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
+        return true;
+    return false;
+}
+
+static bool platformExcludesSharedCache_tvOS(const char* installName) {
+    return platformExcludesSharedCache_iOS(installName);
+}
+
+static bool platformExcludesSharedCache_watchOS(const char* installName) {
+    return platformExcludesSharedCache_iOS(installName);
+}
+
+static bool platformExcludesSharedCache_bridgeOS(const char* installName) {
+    return platformExcludesSharedCache_iOS(installName);
+}
+
+// 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) {
+    switch (platform) {
+        case dyld3::Platform::unknown:
+            return false;
+        case dyld3::Platform::macOS:
+            return platformExcludesSharedCache_macOS(installName);
+        case dyld3::Platform::iOS:
+            return platformExcludesSharedCache_iOS(installName);
+        case dyld3::Platform::tvOS:
+            return platformExcludesSharedCache_tvOS(installName);
+        case dyld3::Platform::watchOS:
+            return platformExcludesSharedCache_watchOS(installName);
+        case dyld3::Platform::bridgeOS:
+            return platformExcludesSharedCache_bridgeOS(installName);
+        case dyld3::Platform::iOSMac:
+            return platformExcludesSharedCache_macOS(installName);
+        case dyld3::Platform::iOS_simulator:
+            return false;
+        case dyld3::Platform::tvOS_simulator:
+            return false;
+        case dyld3::Platform::watchOS_simulator:
+            return false;
+        case dyld3::Platform::driverKit:
+            return false;
+    }
+}
+
+
+
 bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
 {
+
     if ( !isSharedCacheEligiblePath(path) ) {
         // Dont spam the user with an error about paths when we know these are never eligible.
         return false;
@@ -1012,6 +1316,28 @@
         failureReason("install path does not match install name");
         return false;
     }
+    else if ( strstr(dylibName, "//") != 0 ) {
+        failureReason("install name should not include //");
+        return false;
+    }
+    else if ( strstr(dylibName, "./") != 0 ) {
+        failureReason("install name should not include ./");
+        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;
+        }
+    });
+    if ( platformExcludedFile ) {
+        failureReason("install name is not shared cache eligible on platform");
+        return false;
+    }
 
     bool retval = true;
 
@@ -1031,6 +1357,7 @@
     __block bool hasExtraInfo = false;
     __block bool hasDyldInfo = false;
     __block bool hasExportTrie = false;
+    __block bool hasLazyLoad = false;
     Diagnostics diag;
     forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
         if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
@@ -1039,6 +1366,8 @@
             hasDyldInfo = true;
         if ( cmd->cmd == LC_DYLD_EXPORTS_TRIE )
             hasExportTrie = true;
+        if ( cmd->cmd == LC_LAZY_LOAD_DYLIB )
+            hasLazyLoad = true;
     });
     if ( !hasExtraInfo ) {
         retval = false;
@@ -1047,6 +1376,10 @@
     if ( !hasDyldInfo && !hasExportTrie ) {
         retval = false;
         failureReason("Old binary, missing dyld info or export trie");
+    }
+    if ( hasLazyLoad ) {
+        retval = false;
+        failureReason("Has lazy load");
     }
 
     // dylib can only depend on other dylibs in the shared cache
@@ -1063,27 +1396,259 @@
     }
 
     // dylibs with interposing info cannot be in cache
+    if ( hasInterposingTuples() ) {
+        retval = false;
+        failureReason("Has interposing tuples");
+    }
+
+    // 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 ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) {
+            retval = false;
+            failureReason("i386 swift binary");
+        }
+    }
+
+    return retval;
+}
+
+#if BUILDING_APP_CACHE_UTIL
+bool MachOFile::canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const
+{
+    // only dylibs and the kernel itself can go in cache
+    if ( this->filetype == MH_EXECUTE ) {
+        // xnu
+    } else if ( this->isKextBundle() ) {
+        // kext's
+    } else {
+        failureReason("Not MH_KEXT_BUNDLE");
+        return false;
+    }
+
+    if ( this->filetype == MH_EXECUTE ) {
+        // xnu
+
+        // two-level namespace binaries cannot go in cache
+        if ( (this->flags & MH_TWOLEVEL) != 0 ) {
+            failureReason("Built with two level namespaces");
+            return false;
+        }
+
+        // xnu kernel cannot have a page zero
+        __block bool foundPageZero = false;
+        forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) {
+            if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 ) {
+                foundPageZero = true;
+                stop = true;
+            }
+        });
+        if (foundPageZero) {
+            failureReason("Has __PAGEZERO");
+            return false;
+        }
+
+        // xnu must have an LC_UNIXTHREAD to point to the entry point
+        __block bool foundMainLC = false;
+        __block bool foundUnixThreadLC = false;
+        Diagnostics diag;
+        forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+            if ( cmd->cmd == LC_MAIN ) {
+                foundMainLC = true;
+                stop = true;
+            }
+            else if ( cmd->cmd == LC_UNIXTHREAD ) {
+                foundUnixThreadLC = true;
+            }
+        });
+        if (foundMainLC) {
+            failureReason("Found LC_MAIN");
+            return false;
+        }
+        if (!foundUnixThreadLC) {
+            failureReason("Expected LC_UNIXTHREAD");
+            return false;
+        }
+
+        if (diag.hasError()) {
+            failureReason("Error parsing load commands");
+            return false;
+        }
+
+        // The kernel should be a static executable, not a dynamic one
+        if ( !isStaticExecutable() ) {
+            failureReason("Expected static executable");
+            return false;
+        }
+
+        // The kernel must be built with -pie
+        if ( !isPIE() ) {
+            failureReason("Expected pie");
+            return false;
+        }
+    }
+
+    if ( isArch("arm64e") && isKextBundle() && !hasChainedFixups() ) {
+        failureReason("Missing fixup information");
+        return false;
+    }
+
+    // dylibs with interposing info cannot be in cache
     __block bool hasInterposing = false;
     forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
         if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
             hasInterposing = true;
     });
     if ( hasInterposing ) {
+        failureReason("Has interposing tuples");
+        return false;
+    }
+
+    // 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) ) {
+                failureReason("Segments are not allowed to be both writable and executable");
+                foundBadSegment = true;
+                stop = true;
+            }
+        });
+        if ( foundBadSegment )
+            return false;
+    }
+
+    return true;
+}
+#endif
+
+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;
+}
+
+static bool platformExcludesPrebuiltClosure_tvOS(const char* path) {
+    return platformExcludesPrebuiltClosure_iOS(path);
+}
+
+static bool platformExcludesPrebuiltClosure_watchOS(const char* path) {
+    return platformExcludesPrebuiltClosure_iOS(path);
+}
+
+static bool platformExcludesPrebuiltClosure_bridgeOS(const char* path) {
+    return platformExcludesPrebuiltClosure_iOS(path);
+}
+
+// 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) {
+    switch (platform) {
+        case dyld3::Platform::unknown:
+            return false;
+        case dyld3::Platform::macOS:
+            return platformExcludesPrebuiltClosure_macOS(path);
+        case dyld3::Platform::iOS:
+            return platformExcludesPrebuiltClosure_iOS(path);
+        case dyld3::Platform::tvOS:
+            return platformExcludesPrebuiltClosure_tvOS(path);
+        case dyld3::Platform::watchOS:
+            return platformExcludesPrebuiltClosure_watchOS(path);
+        case dyld3::Platform::bridgeOS:
+            return platformExcludesPrebuiltClosure_bridgeOS(path);
+        case dyld3::Platform::iOSMac:
+            return platformExcludesPrebuiltClosure_macOS(path);
+        case dyld3::Platform::iOS_simulator:
+            return false;
+        case dyld3::Platform::tvOS_simulator:
+            return false;
+        case dyld3::Platform::watchOS_simulator:
+            return false;
+        case dyld3::Platform::driverKit:
+            return false;
+    }
+}
+
+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("Has interposing tuples");
-    }
-
-    // Temporarily kick out swift binaries on watchOS simulators as they have missing split seg
-    if ( supportsPlatform(Platform::watchOS_simulator) && isArch("i386") ) {
-        if ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) {
-            retval = false;
-            failureReason("i386 swift binary");
-        }
+        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;
 }
 
+bool MachOFile::hasInterposingTuples() const
+{
+    __block bool hasInterposing = false;
+    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
+            hasInterposing = true;
+    });
+    return hasInterposing;
+}
 
 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
 {
@@ -1151,9 +1716,16 @@
 {
 #if SUPPORT_ARCH_arm64e
     // arm64e always uses chained fixups
-    if ( (this->cputype == CPU_TYPE_ARM64) && (this->cpusubtype == CPU_SUBTYPE_ARM64E) )
-        return true;
-#endif
+    if ( (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
+        // Not all binaries have fixups at all so check for the load commands
+        return hasLoadCommand(LC_DYLD_INFO_ONLY) || hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
+    }
+#endif
+    return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
+}
+
+bool MachOFile::hasChainedFixupsLoadCommand() const
+{
     return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
 }
 
@@ -1197,7 +1769,7 @@
         bit += 7;
     } while (byte & 0x80);
     // sign extend negative numbers
-    if ( (byte & 0x40) != 0 )
+    if ( ((byte & 0x40) != 0) && (bit < 64) )
         result |= (~0ULL) << bit;
     return result;
 }