Loading...
cache-builder/AppCacheBuilder.cpp dyld-1286.10 dyld-960
--- dyld/dyld-1286.10/cache-builder/AppCacheBuilder.cpp
+++ dyld/dyld-960/cache-builder/AppCacheBuilder.cpp
@@ -36,10 +36,6 @@
 #include <CommonCrypto/CommonDigest.h>
 #include <CommonCrypto/CommonDigestSPI.h>
 
-#include "Header.h"
-
-using mach_o::Header;
-
 AppCacheBuilder::AppCacheBuilder(const DyldSharedCache::CreateOptions& options,
                                  const Options& appCacheOptions,
                                  const dyld3::closure::FileSystem& fileSystem)
@@ -109,7 +105,7 @@
     bool stop = false;
     for (const AppCacheDylibInfo& dylib : sortedDylibs) {
         for (const SegmentMappingInfo& loc : dylib.cacheLocation) {
-            if ( loc.segName == "__TEXT" ) {
+            if (!strcmp(loc.segName, "__TEXT")) {
                 // Assume __TEXT contains the mach header
                 callback((const dyld3::MachOAnalyzer*)loc.dstSegment, dylib.dylibID, dylib.stripMode,
                          dylib.dependencies, *dylib.errors, stop);
@@ -121,11 +117,9 @@
     }
 }
 
-void AppCacheBuilder::forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag,
-                                                        cache_builder::ASLR_Tracker& dylibASLRTracker,
-                                                        const CacheBuilder::DylibSectionCoalescer* sectionCoalescer)) {
+void AppCacheBuilder::forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag, ASLR_Tracker& dylibASLRTracker)) {
     for (const AppCacheDylibInfo& dylibInfo : sortedDylibs)
-        callback(dylibInfo, *dylibInfo.errors, _aslrTracker, &dylibInfo._coalescer);
+        callback(dylibInfo, *dylibInfo.errors, _aslrTracker);
 }
 
 const CacheBuilder::DylibInfo* AppCacheBuilder::getKernelStaticExecutableInputFile() const {
@@ -163,45 +157,33 @@
     // readOnlyTextRegion
     callback(readOnlyTextRegion);
 
+    // readExecuteRegion
+    if ( readExecuteRegion.sizeInUse != 0 )
+        callback(readExecuteRegion);
+
+    // branchStubsRegion
+    if ( branchStubsRegion.bufferSize != 0 )
+        callback(branchStubsRegion);
+
+    // dataConstRegion
+    if ( dataConstRegion.sizeInUse != 0 )
+        callback(dataConstRegion);
+
+    // branchGOTsRegion
+    if ( branchGOTsRegion.bufferSize != 0 )
+        callback(branchGOTsRegion);
+
+    // readWriteRegion
+    if ( readWriteRegion.sizeInUse != 0 )
+        callback(readWriteRegion);
+
+    // hibernateRegion
+    if ( hibernateRegion.sizeInUse != 0 )
+        callback(hibernateRegion);
+
     // -sectcreate
     for (const Region& region : customDataRegions)
         callback(region);
-
-    // readExecuteRegion
-    if ( readExecuteRegion.sizeInUse != 0 )
-        callback(readExecuteRegion);
-
-    // branchStubsRegion
-    if ( branchStubsRegion.bufferSize != 0 )
-        callback(branchStubsRegion);
-
-    // textBootExecRegion
-    if ( textBootExecRegion.sizeInUse != 0 )
-        callback(textBootExecRegion);
-
-    // dataConstRegion
-    if ( dataConstRegion.sizeInUse != 0 )
-        callback(dataConstRegion);
-
-    // lateConstRegion
-    if ( lateConstRegion.sizeInUse != 0 )
-        callback(lateConstRegion);
-
-    // dataSptmRegion
-    if ( dataSptmRegion.sizeInUse != 0 )
-        callback(dataSptmRegion);
-
-    // branchGOTsRegion
-    if ( branchGOTsRegion.bufferSize != 0 )
-        callback(branchGOTsRegion);
-
-    // readWriteRegion
-    if ( readWriteRegion.sizeInUse != 0 )
-        callback(readWriteRegion);
-
-    // hibernateRegion
-    if ( hibernateRegion.sizeInUse != 0 )
-        callback(hibernateRegion);
 
     // prelinkInfoRegion
     if ( prelinkInfoDict != nullptr )
@@ -299,94 +281,31 @@
     return totalTargets;
 }
 
-bool AppCacheBuilder::hasSancovGateSection() const
-{
-    for ( const AppCacheDylibInfo& dylib : sortedDylibs ) {
-        const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh;
-        if ( ma->hasSection("__DATA", "__sancov_gate") )
-            return true;
-    }
-
-    return false;
-}
-
-bool AppCacheBuilder::removeStubs()
-{
-    // Only eliminate stubs in the base kernel collection.  We could eliminate stubs
-    // in the auxKC too, for those calls resolved within the auxKC, but its not worth it right now
-    if ( appCacheOptions.cacheKind != Options::AppCacheKind::kernel )
-        return false;
-
-    if ( _options.archs != &dyld3::GradedArchs::arm64e )
-        return false;
-
-    if ( hasSancovGateSection() )
-        return false;
-
-    return true;
-}
-
-void AppCacheBuilder::parseStubs()
-{
-    if ( !removeStubs() )
-        return;
-
-    for (AppCacheDylibInfo& dylib : sortedDylibs) {
-
-        const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh;
-
-        // We can only remove sections if we know we have split seg v2 to point to it
-        uint32_t splitSegSize = 0;
-        const void* splitSegStart = ma->getSplitSeg(splitSegSize);
-        if (!splitSegStart)
-            continue;
-
-        if ((*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT)
-            continue;
-
-        // Find __TEXT_EXEC __auth_stubs, and remove it if we have it
-        __block bool lastSectionWasAuthStubs = false;
-        ((const Header*)ma)->forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
-            if ( sectInfo.segmentName != "__TEXT_EXEC" )
-                return;
-            lastSectionWasAuthStubs = false;
-            if ( sectInfo.sectionName == "__auth_stubs" ) {
-                // The auth stubs are only valid if the sections is 16-byte stubs
-                if ( ((sectInfo.flags & SECTION_TYPE) == S_SYMBOL_STUBS) && (sectInfo.reserved2 == 16) )
-                    lastSectionWasAuthStubs = true;
-            }
-        });
-
-        if ( lastSectionWasAuthStubs )
-            dylib._coalescer.auth_stubs.sectionIsObliterated = true;
-    }
-}
-
 void AppCacheBuilder::assignSegmentRegionsAndOffsets()
 {
     // Segments can be re-ordered in memory relative to the order of the LC_SEGMENT load comamnds
     // so first make space for all the cache location objects so that we get the order the same
     // as the LC_SEGMENTs
     for (DylibInfo& dylib : sortedDylibs) {
-        ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, bool& stop) {
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
             dylib.cacheLocation.push_back({});
         });
     }
 
     // If we are building the kernel collection, then inherit the base address of the statically linked kernel
-    const Header* kernelHdr = nullptr;
+    const dyld3::MachOAnalyzer* kernelMA = nullptr;
     if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) {
         for (DylibInfo& dylib : sortedDylibs) {
             if ( dylib.input->mappedFile.mh->isStaticExecutable() ) {
-                kernelHdr = (const Header*)dylib.input->mappedFile.mh;
+                kernelMA = dylib.input->mappedFile.mh;
                 break;
             }
         }
-        if ( kernelHdr == nullptr ) {
+        if ( kernelMA == nullptr ) {
             _diagnostics.error("Could not find kernel image");
             return;
         }
-        cacheBaseAddress = kernelHdr->preferredLoadAddress();
+        cacheBaseAddress = kernelMA->preferredLoadAddress();
     }
 
     // x86_64 doesn't have stubs for kext branches.  So work out how many potential targets
@@ -398,10 +317,10 @@
         minimumSegmentAlignmentP2 = 12;
     }
 
-    auto getMinAlignment = ^(const Header* hdr) {
+    auto getMinAlignment = ^(const dyld3::MachOAnalyzer* ma) {
         // The kernel wants to be able to unmap its own segments so always align it.
         // And align the pageable KC as each kext can be mapped individually
-        if ( hdr == kernelHdr )
+        if ( ma == kernelMA )
             return minimumSegmentAlignmentP2;
         if ( fixupsArePerKext() )
             return minimumSegmentAlignmentP2;
@@ -417,27 +336,26 @@
                 continue;
 
             __block uint64_t textSegVmAddr = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( segInfo.initProt != (VM_PROT_READ) )
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( segInfo.protections != (VM_PROT_READ) )
                     return;
-                if (   ( segInfo.segmentName == "__DATA_CONST"     )
-                    || ( segInfo.segmentName == "__PPLDATA_CONST"  )
-                    || ( segInfo.segmentName == "__LASTDATA_CONST" )
-                    || ( segInfo.segmentName == "__LATE_CONST"     ) )
+                if ( (strcmp(segInfo.segName, "__DATA_CONST") == 0)
+                    || (strcmp(segInfo.segName, "__PPLDATA_CONST") == 0)
+                    || (strcmp(segInfo.segName, "__LASTDATA_CONST") == 0) )
                     return;
-                if ( segInfo.segmentName == "__LINKEDIT" )
+                if ( strcmp(segInfo.segName, "__LINKEDIT") == 0 )
                     return;
-                if ( segInfo.segmentName == "__LINKINFO" )
+                if ( strcmp(segInfo.segName, "__LINKINFO") == 0 )
                     return;
 
-                uint32_t minAlignmentP2 = getMinAlignment((const Header*)dylib.input->mappedFile.mh);
-                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
-                uint64_t dstCacheSegmentSize = align(sizeOfSections, minAlignmentP2);
+                uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2);
 
                 // __CTF is not mapped in to the kernel, so remove it from the final binary.
-                if ( segInfo.segmentName == "__CTF" ) {
+                if ( strcmp(segInfo.segName, "__CTF") == 0 ) {
                     copySize = 0;
                     dstCacheSegmentSize = 0;
                 }
@@ -445,20 +363,20 @@
                 // kxld packs __TEXT so we will do
                 // Note we align to at least 16-bytes as LDR's can scale up to 16 from their address
                 // and aligning them less than 16 would break that
-                offsetInRegion = align(offsetInRegion, std::max(maxAlignOfSections, 4U));
+                offsetInRegion = align(offsetInRegion, std::max(segInfo.p2align, 4U));
                 offsetInRegion = align(offsetInRegion, minAlignmentP2);
                 SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
                 loc.dstSegment             = nullptr;
                 loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
                 loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
                 loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
                 loc.dstCacheFileSize       = (uint32_t)copySize;
                 loc.copySegmentSize        = (uint32_t)copySize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
+                loc.srcSegmentIndex        = segInfo.segIndex;
                 loc.parentRegion           = &readOnlyTextRegion;
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
                 offsetInRegion += dstCacheSegmentSize;
             });
         }
@@ -475,57 +393,39 @@
     {
         // __TEXT segments with r/x permissions
         __block uint64_t offsetInRegion = 0;
-        for (AppCacheDylibInfo& dylib : sortedDylibs) {
+        for (DylibInfo& dylib : sortedDylibs) {
             bool canBePacked = dylib.input->mappedFile.mh->hasSplitSeg();
             if (!canBePacked)
                 continue;
 
             __block uint64_t textSegVmAddr = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t segSizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( segInfo.segmentName == "__HIB" )
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( strcmp(segInfo.segName, "__HIB") == 0 )
                     return;
-                if ( (segInfo.segmentName == "__TEXT_BOOT_EXEC" ) && dylib.input->mappedFile.mh->isStaticExecutable() )
-                    return;
-                if ( segInfo.initProt != (VM_PROT_READ | VM_PROT_EXECUTE) )
+                if ( segInfo.protections != (VM_PROT_READ | VM_PROT_EXECUTE) )
                    return;
-
-                // We may have coalesced the sections at the end of this segment.  In that case, shrink the segment to remove them.
-                __block size_t sizeOfSections = 0;
-                __block bool foundRemovedSection = false;
-                ((const Header*)dylib.input->mappedFile.mh)->forEachSection(^(const Header::SectionInfo &sectInfo, bool &stopSection) {
-                    if ( sectInfo.segmentName != segInfo.segmentName )
-                        return;
-                    if ( dylib._coalescer.sectionWasObliterated(segInfo.segmentName, sectInfo.sectionName) ) {
-                        foundRemovedSection = true;
-                    } else {
-                        sizeOfSections = sectInfo.address + sectInfo.size - segInfo.vmaddr;
-                    }
-                });
-                if ( !foundRemovedSection )
-                    sizeOfSections = segSizeOfSections;
-
                 // kxld packs __TEXT_EXEC so we will do
                 // Note we align to at least 16-bytes as LDR's can scale up to 16 from their address
                 // and aligning them less than 16 would break that
-                uint32_t minAlignmentP2 = getMinAlignment((const Header*)dylib.input->mappedFile.mh);
-                offsetInRegion = align(offsetInRegion, std::max(maxAlignOfSections, 4U));
+                uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh);
+                offsetInRegion = align(offsetInRegion, std::max(segInfo.p2align, 4U));
                 offsetInRegion = align(offsetInRegion, minAlignmentP2);
-                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
-                uint64_t dstCacheSegmentSize = align(sizeOfSections, minAlignmentP2);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2);
                 SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
                 loc.dstSegment             = nullptr;
                 loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
                 loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
                 loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
                 loc.dstCacheFileSize       = (uint32_t)copySize;
                 loc.copySegmentSize        = (uint32_t)copySize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
+                loc.srcSegmentIndex        = segInfo.segIndex;
                 loc.parentRegion           = &readExecuteRegion;
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
                 offsetInRegion += loc.dstCacheSegmentSize;
             });
         }
@@ -547,111 +447,41 @@
         branchStubsRegion.name          = "__BRANCH_STUBS";
     }
 
-    // __TEXT_BOOT_EXEC segments in xnu with r/x permissions
+    // __DATA_CONST segments
     {
         __block uint64_t offsetInRegion = 0;
-        for (AppCacheDylibInfo& dylib : sortedDylibs) {
-            bool canBePacked = dylib.input->mappedFile.mh->hasSplitSeg();
-            if (!canBePacked)
+        for (DylibInfo& dylib : sortedDylibs) {
+            if (!dylib.input->mappedFile.mh->hasSplitSeg())
                 continue;
 
-            // Only do this for xnu
-            if ( !dylib.input->mappedFile.mh->isStaticExecutable() )
-                continue;
-
             __block uint64_t textSegVmAddr = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t segSizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( segInfo.segmentName != "__TEXT_BOOT_EXEC" )
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( (segInfo.protections & VM_PROT_EXECUTE) != 0 )
                     return;
-                if ( segInfo.initProt != (VM_PROT_READ | VM_PROT_EXECUTE) )
+                if ( (strcmp(segInfo.segName, "__DATA_CONST") != 0)
+                    && (strcmp(segInfo.segName, "__PPLDATA_CONST") != 0)
+                    && (strcmp(segInfo.segName, "__LASTDATA_CONST") != 0)  )
                     return;
-
-                // We may have coalesced the sections at the end of this segment.  In that case, shrink the segment to remove them.
-                __block size_t sizeOfSections = 0;
-                __block bool foundRemovedSection = false;
-                ((const Header*)dylib.input->mappedFile.mh)->forEachSection(^(const Header::SectionInfo &sectInfo, bool &stopSection) {
-                    if ( sectInfo.segmentName != segInfo.segmentName )
-                        return;
-                    if ( dylib._coalescer.sectionWasObliterated(segInfo.segmentName, sectInfo.sectionName)) {
-                        foundRemovedSection = true;
-                    } else {
-                        sizeOfSections = sectInfo.address + sectInfo.size - segInfo.vmaddr;
-                    }
-                });
-                if ( !foundRemovedSection )
-                    sizeOfSections = segSizeOfSections;
-
-                // kxld packs __TEXT_EXEC so we will do
-                // Note we align to at least 16-bytes as LDR's can scale up to 16 from their address
-                // and aligning them less than 16 would break that
-                uint32_t minAlignmentP2 = getMinAlignment((const Header*)dylib.input->mappedFile.mh);
-                offsetInRegion = align(offsetInRegion, std::max(maxAlignOfSections, 4U));
+                // kxld packs __DATA_CONST so we will do
+                uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh);
+                offsetInRegion = align(offsetInRegion, segInfo.p2align);
                 offsetInRegion = align(offsetInRegion, minAlignmentP2);
-                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
-                uint64_t dstCacheSegmentSize = align(sizeOfSections, minAlignmentP2);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2);
                 SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
                 loc.dstSegment             = nullptr;
                 loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
                 loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
                 loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
                 loc.dstCacheFileSize       = (uint32_t)copySize;
                 loc.copySegmentSize        = (uint32_t)copySize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
-                loc.parentRegion           = &textBootExecRegion;
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
-                offsetInRegion += loc.dstCacheSegmentSize;
-            });
-        }
-
-        // align r/x region end
-        textBootExecRegion.bufferSize  = align(offsetInRegion, 14);
-        textBootExecRegion.sizeInUse   = textBootExecRegion.bufferSize;
-        textBootExecRegion.initProt    = VM_PROT_READ | VM_PROT_EXECUTE;
-        textBootExecRegion.maxProt     = VM_PROT_READ | VM_PROT_EXECUTE;
-        textBootExecRegion.name        = "__TEXT_BOOT_EXEC";
-    }
-
-    // __DATA_CONST segments
-    {
-        __block uint64_t offsetInRegion = 0;
-        for (DylibInfo& dylib : sortedDylibs) {
-            if (!dylib.input->mappedFile.mh->hasSplitSeg())
-                continue;
-
-            __block uint64_t textSegVmAddr = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( (segInfo.initProt & VM_PROT_EXECUTE) != 0 )
-                    return;
-                if (   ( segInfo.segmentName != "__DATA_CONST"    )
-                    && ( segInfo.segmentName != "__PPLDATA_CONST" )
-                    && ( segInfo.segmentName != "__LASTDATA_CONST") )
-                    return;
-                if ( segInfo.segmentName == "__LATE_CONST" )
-                    return;
-                // kxld packs __DATA_CONST so we will do
-                uint32_t minAlignmentP2 = getMinAlignment((const Header*)dylib.input->mappedFile.mh);
-                offsetInRegion = align(offsetInRegion, maxAlignOfSections);
-                offsetInRegion = align(offsetInRegion, minAlignmentP2);
-                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
-                uint64_t dstCacheSegmentSize = align(sizeOfSections, minAlignmentP2);
-                SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
-                loc.dstSegment             = nullptr;
-                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
-                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
-                loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
-                loc.dstCacheFileSize       = (uint32_t)copySize;
-                loc.copySegmentSize        = (uint32_t)copySize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
+                loc.srcSegmentIndex        = segInfo.segIndex;
                 loc.parentRegion           = &dataConstRegion;
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
                 offsetInRegion += loc.dstCacheSegmentSize;
             });
         }
@@ -663,96 +493,6 @@
         dataConstRegion.maxProt     = VM_PROT_READ;
         dataConstRegion.name        = "__DATA_CONST";
     }
-
-    // __LATE_CONST segments
-    {
-        __block uint64_t offsetInRegion = 0;
-        for (DylibInfo& dylib : sortedDylibs) {
-            if (!dylib.input->mappedFile.mh->hasSplitSeg())
-                continue;
-
-            __block uint64_t textSegVmAddr = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( (segInfo.initProt & VM_PROT_EXECUTE) != 0 )
-                    return;
-                if ( segInfo.segmentName != "__LATE_CONST" )
-                    return;
-                // pack __LATE_CONST
-                uint32_t minAlignmentP2 = getMinAlignment((const Header*)dylib.input->mappedFile.mh);
-                offsetInRegion = align(offsetInRegion, maxAlignOfSections);
-                offsetInRegion = align(offsetInRegion, minAlignmentP2);
-                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
-                uint64_t dstCacheSegmentSize = align(sizeOfSections, minAlignmentP2);
-                SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
-                loc.dstSegment             = nullptr;
-                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
-                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
-                loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
-                loc.dstCacheFileSize       = (uint32_t)copySize;
-                loc.copySegmentSize        = (uint32_t)copySize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
-                loc.parentRegion           = &lateConstRegion;
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
-                offsetInRegion += loc.dstCacheSegmentSize;
-            });
-        }
-
-        // align r/o region end
-        lateConstRegion.bufferSize  = align(offsetInRegion, 14);
-        lateConstRegion.sizeInUse   = lateConstRegion.bufferSize;
-        lateConstRegion.initProt    = VM_PROT_READ;
-        lateConstRegion.maxProt     = VM_PROT_READ;
-        lateConstRegion.name        = "__LATE_CONST";
-    }
-
-    // __DATA_SPTM segments
-    {
-        __block uint64_t offsetInRegion = 0;
-        for (DylibInfo& dylib : sortedDylibs) {
-            if (!dylib.input->mappedFile.mh->hasSplitSeg())
-                continue;
-
-            __block uint64_t textSegVmAddr = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( (segInfo.initProt & VM_PROT_EXECUTE) != 0 )
-                    return;
-                if ( segInfo.segmentName != "__DATA_SPTM" )
-                    return;
-                uint32_t minAlignmentP2 = getMinAlignment((const Header*)dylib.input->mappedFile.mh);
-                offsetInRegion = align(offsetInRegion, maxAlignOfSections);
-                offsetInRegion = align(offsetInRegion, minAlignmentP2);
-                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
-                uint64_t dstCacheSegmentSize = align(sizeOfSections, minAlignmentP2);
-                SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
-                loc.dstSegment             = nullptr;
-                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
-                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
-                loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
-                loc.dstCacheFileSize       = (uint32_t)copySize;
-                loc.copySegmentSize        = (uint32_t)copySize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
-                loc.parentRegion           = &dataSptmRegion;
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
-                offsetInRegion += loc.dstCacheSegmentSize;
-            });
-        }
-
-        // align r/o region end
-        dataSptmRegion.bufferSize  = align(offsetInRegion, 14);
-        dataSptmRegion.sizeInUse   = dataSptmRegion.bufferSize;
-        dataSptmRegion.initProt    = VM_PROT_READ;
-        dataSptmRegion.maxProt     = VM_PROT_READ;
-        dataSptmRegion.name        = "__DATA_SPTM";
-    }
-
 
     // Branch GOTs
     if ( branchTargetsFromKexts != 0 ) {
@@ -772,36 +512,35 @@
                 continue;
 
             __block uint64_t textSegVmAddr = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( segInfo.segmentName == "__HIB" )
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( strcmp(segInfo.segName, "__HIB") == 0 )
                     return;
-                if (   ( segInfo.segmentName == "__DATA_CONST"     )
-                    || ( segInfo.segmentName == "__PPLDATA_CONST"  )
-                    || ( segInfo.segmentName == "__LASTDATA_CONST" )
-                    || ( segInfo.segmentName == "__LATE_CONST"     ) )
+                if ( (strcmp(segInfo.segName, "__DATA_CONST") == 0)
+                    || (strcmp(segInfo.segName, "__PPLDATA_CONST") == 0)
+                    || (strcmp(segInfo.segName, "__LASTDATA_CONST") == 0) )
                     return;
-                if ( segInfo.initProt != (VM_PROT_READ | VM_PROT_WRITE) )
+                if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
                     return;
                 // kxld packs __DATA so we will do
-                uint32_t minAlignmentP2 = getMinAlignment((const Header*)dylib.input->mappedFile.mh);
-                offsetInRegion = align(offsetInRegion, maxAlignOfSections);
+                uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh);
+                offsetInRegion = align(offsetInRegion, segInfo.p2align);
                 offsetInRegion = align(offsetInRegion, minAlignmentP2);
-                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
-                uint64_t dstCacheSegmentSize = align(sizeOfSections, minAlignmentP2);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2);
                 SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
                 loc.dstSegment             = nullptr;
                 loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
                 loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
                 loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
                 loc.dstCacheFileSize       = (uint32_t)copySize;
                 loc.copySegmentSize        = (uint32_t)copySize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
+                loc.srcSegmentIndex        = segInfo.segIndex;
                 loc.parentRegion           = &readWriteRegion;
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
                 offsetInRegion += loc.dstCacheSegmentSize;
             });
         }
@@ -822,44 +561,28 @@
                 continue;
 
             __block uint64_t textSegVmAddr = 0;
-            __block uint64_t hibernateAddress = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( segInfo.segmentName != "__HIB" )
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( strcmp(segInfo.segName, "__HIB") != 0 )
                     return;
-                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
                 SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
                 loc.dstSegment             = nullptr;
                 loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
                 loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
-                loc.dstCacheSegmentSize    = (uint32_t)segInfo.vmsize;
+                loc.dstCacheSegmentSize    = (uint32_t)segInfo.vmSize;
                 loc.dstCacheFileSize       = (uint32_t)copySize;
                 loc.copySegmentSize        = (uint32_t)copySize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
+                loc.srcSegmentIndex        = segInfo.segIndex;
                 loc.parentRegion           = &hibernateRegion;
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
                 offsetInRegion += loc.dstCacheSegmentSize;
 
-                hibernateAddress = segInfo.vmaddr;
+                hibernateAddress = segInfo.vmAddr;
             });
-
-            if ( offsetInRegion != 0 ) {
-                // Pad out the VM offset so that the cache header starts where the base address
-                // really should be
-                uint64_t paddedSize = cacheBaseAddress - hibernateAddress;
-                if ( offsetInRegion > paddedSize ) {
-                    _diagnostics.error("Could not lay out __HIB segment");
-                    return;
-                }
-                offsetInRegion = paddedSize;
-
-                // Set the base address to the hibernate address so that we actually put the
-                // hibernate segment there
-                cacheBaseAddress = hibernateAddress;
-            }
 
             // Only xnu has __HIB, so no need to continue once we've found it.
             break;
@@ -880,31 +603,31 @@
                 continue;
 
             __block uint64_t textSegVmAddr = 0;
-            ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, bool& stop) {
-                if ( segInfo.segmentName == "__TEXT" )
-                    textSegVmAddr = segInfo.vmaddr;
-                if ( segInfo.segmentName == "__LINKEDIT" )
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( strcmp(segInfo.segName, "__LINKEDIT") == 0 )
                     return;
 
                 nonSplitSegRegions.emplace_back();
-                nonSplitSegRegions.back().initProt    = segInfo.initProt;
-                nonSplitSegRegions.back().maxProt     = segInfo.maxProt;
+                nonSplitSegRegions.back().initProt    = segInfo.protections;
+                nonSplitSegRegions.back().maxProt     = segInfo.protections;
                 nonSplitSegRegions.back().name        = "__REGION" + std::to_string(nonSplitSegRegions.size() - 1);
 
                 // Note we don't align the region offset as we have no split seg
                 uint64_t offsetInRegion = 0;
                 SegmentMappingInfo loc;
-                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-                loc.segName                = segInfo.segmentName;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
                 loc.dstSegment             = nullptr;
                 loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
                 loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
-                loc.dstCacheSegmentSize    = (uint32_t)segInfo.vmsize;
+                loc.dstCacheSegmentSize    = (uint32_t)segInfo.vmSize;
                 loc.dstCacheFileSize       = (uint32_t)segInfo.fileSize;
                 loc.copySegmentSize        = (uint32_t)segInfo.fileSize;
-                loc.srcSegmentIndex        = segInfo.segmentIndex;
+                loc.srcSegmentIndex        = segInfo.segIndex;
                 loc.parentRegion           = &nonSplitSegRegions.back();
-                dylib.cacheLocation[segInfo.segmentIndex] = loc;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
                 offsetInRegion += loc.dstCacheSegmentSize;
 
                 // record non-split seg region end
@@ -993,16 +716,16 @@
             }
 
             // _PrelinkExecutableSize
-            // Use the size of the TEXT sections in the cache.  This is required as we pack segments
-            __block uint64_t textSegSize = 0;
+            // This seems to be the file size of __TEXT
+            __block uint64_t textSegFileSize = 0;
             if ( info.ma != nullptr ) {
-                ((const Header*)info.ma)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-                    if ( segInfo.segmentName == "__TEXT" )
-                        textSegSize = sizeOfSections == 0 ? segInfo.fileSize : sizeOfSections;
+                info.ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                    if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                        textSegFileSize = segInfo.fileSize;
                 });
             }
-            if (textSegSize != 0) {
-                CFNumberRef fileSizeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &textSegSize);
+            if (textSegFileSize != 0) {
+                CFNumberRef fileSizeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &textSegFileSize);
                 CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableSize"), fileSizeRef);
                 CFRelease(fileSizeRef);
             }
@@ -1072,7 +795,7 @@
         // The pageable/aux KCs should embed the UUID of the base kernel collection
         if ( existingKernelCollection != nullptr ) {
             uuid_t uuid = {};
-            bool foundUUID = ((mach_o::Header*)existingKernelCollection)->getUuid(uuid);
+            bool foundUUID = existingKernelCollection->getUuid(uuid);
             if ( !foundUUID ) {
                 _diagnostics.error("Could not find UUID in base kernel collection");
                 return;
@@ -1085,7 +808,7 @@
         // The aux KC should embed the UUID of the pageable kernel collection if we have one
         if ( pageableKernelCollection != nullptr ) {
             uuid_t uuid = {};
-            bool foundUUID = ((mach_o::Header*)pageableKernelCollection)->getUuid(uuid);
+            bool foundUUID = pageableKernelCollection->getUuid(uuid);
             if ( !foundUUID ) {
                 _diagnostics.error("Could not find UUID in pageable kernel collection");
                 return;
@@ -1123,28 +846,28 @@
     __block uint64_t offsetInRegion = 0;
     for (DylibInfo& dylib : sortedDylibs) {
         __block uint64_t textSegVmAddr = 0;
-        ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-            if ( segInfo.segmentName == "__TEXT" )
-                textSegVmAddr = segInfo.vmaddr;
-            if ( segInfo.initProt != VM_PROT_READ )
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.protections != VM_PROT_READ )
                 return;
-            if ( segInfo.segmentName != "__LINKINFO" )
+            if ( strcmp(segInfo.segName, "__LINKINFO") != 0 )
                 return;
             // Keep segments 4K or more aligned
-            offsetInRegion = align(offsetInRegion, std::max(maxAlignOfSections, 12U));
-            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
+            offsetInRegion = align(offsetInRegion, std::max((int)segInfo.p2align, (int)12));
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
             SegmentMappingInfo loc;
-            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-            loc.segName                = segInfo.segmentName;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
             loc.dstSegment             = nullptr;
             loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
             loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
-            loc.dstCacheSegmentSize    = (uint32_t)align(sizeOfSections, 12);
+            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
             loc.dstCacheFileSize       = (uint32_t)copySize;
             loc.copySegmentSize        = (uint32_t)copySize;
-            loc.srcSegmentIndex        = segInfo.segmentIndex;
+            loc.srcSegmentIndex        = segInfo.segIndex;
             loc.parentRegion           = &_readOnlyRegion;
-            dylib.cacheLocation[segInfo.segmentIndex] = loc;
+            dylib.cacheLocation[segInfo.segIndex] = loc;
             offsetInRegion += loc.dstCacheSegmentSize;
         });
     }
@@ -1156,36 +879,28 @@
     // Do all __LINKEDIT, regardless of split seg
     for (DylibInfo& dylib : sortedDylibs) {
         __block uint64_t textSegVmAddr = 0;
-        ((const Header*)dylib.input->mappedFile.mh)->forEachSegment(^(const Header::SegmentInfo& segInfo, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop) {
-            if ( segInfo.segmentName == "__TEXT" )
-                textSegVmAddr = segInfo.vmaddr;
-            if ( segInfo.initProt != VM_PROT_READ )
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.protections != VM_PROT_READ )
                 return;
-            if ( segInfo.segmentName != "__LINKEDIT" )
+            if ( strcmp(segInfo.segName, "__LINKEDIT") != 0 )
                 return;
             // Keep segments 4K or more aligned
-            offsetInRegion = align(offsetInRegion, std::max(maxAlignOfSections, 12U));
-            size_t copySize = segInfo.fileSize;
-
-            // HACK: When we adjust LINKEDIT, we may grow function starts.  This is because the kernel and kexts
-            // have __TEXT_EXEC __text (or other __text) which is going to be moved away from __TEXT.
-            // That is, the first function start is effectively going to be an offset from __TEXT to __TEXT_EXEC
-            // and that may grow.
-            // Everything is ULEB encoded. We've only seen a need for 8-bytes more, but use 16-bytes to be safe
-            const uint32_t extraLinkeditSpace = 16;
-
+            offsetInRegion = align(offsetInRegion, std::max((int)segInfo.p2align, (int)12));
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
             SegmentMappingInfo loc;
-            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmaddr - textSegVmAddr;
-            loc.segName                = segInfo.segmentName;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
             loc.dstSegment             = nullptr;
             loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
             loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
-            loc.dstCacheSegmentSize    = (uint32_t)align(copySize + extraLinkeditSpace, 12);
-            loc.dstCacheFileSize       = (uint32_t)copySize + extraLinkeditSpace;
+            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
+            loc.dstCacheFileSize       = (uint32_t)copySize;
             loc.copySegmentSize        = (uint32_t)copySize;
-            loc.srcSegmentIndex        = segInfo.segmentIndex;
+            loc.srcSegmentIndex        = segInfo.segIndex;
             loc.parentRegion           = &_readOnlyRegion;
-            dylib.cacheLocation[segInfo.segmentIndex] = loc;
+            dylib.cacheLocation[segInfo.segIndex] = loc;
             offsetInRegion += loc.dstCacheSegmentSize;
         });
     }
@@ -1203,11 +918,13 @@
         // The pageableKC (and sometimes auxKC) has 1 LC_DYLD_CHAINED_FIXUPS per kext
         // while other KCs have 1 for the whole KC.
         // It also tracks each segment in each kext for chained fixups, not the segments on the KC itself
-        uint64_t numSegmentsForChainedFixups = 0;
+        __block uint64_t numSegmentsForChainedFixups = 0;
         uint64_t numChainedFixupHeaders = 0;
         if ( fixupsArePerKext() ) {
             for (DylibInfo& dylib : sortedDylibs) {
-                numSegmentsForChainedFixups += ((const Header*)dylib.input->mappedFile.mh)->segmentCount();
+                dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                    ++numSegmentsForChainedFixups;
+                });
             }
             numChainedFixupHeaders = sortedDylibs.size();
 
@@ -1223,10 +940,6 @@
         uint64_t numBytesForPageStarts = 0;
         if ( dataConstRegion.sizeInUse != 0 )
             numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(dataConstRegion.bufferSize));
-        if ( lateConstRegion.sizeInUse != 0 )
-            numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(lateConstRegion.bufferSize));
-        if ( dataSptmRegion.sizeInUse != 0 )
-            numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(dataSptmRegion.bufferSize));
         if ( branchGOTsRegion.bufferSize != 0 )
             numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(branchGOTsRegion.bufferSize));
         if ( readWriteRegion.sizeInUse != 0 )
@@ -1394,17 +1107,16 @@
                                    std::map<std::string, DylibSymbols>& dylibsToSymbols);
     void findVTables(uint8_t currentLevel, const dyld3::MachOAnalyzer* kernelMA,
                      std::map<std::string, DylibSymbols>& dylibsToSymbols,
-                     const cache_builder::ASLR_Tracker& aslrTracker,
+                     const AppCacheBuilder::ASLR_Tracker& aslrTracker,
                      const std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations);
     void calculateSymbols();
     void patchVTables(Diagnostics& diags,
                       std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations,
-                      cache_builder::ASLR_Tracker& aslrTracker,
+                      AppCacheBuilder::ASLR_Tracker& aslrTracker,
                       uint8_t currentLevel);
 
 private:
 
-    __attribute__((format(printf, 2, 3)))
     void logFunc(const char* format, ...) {
         if ( logPatching ) {
             va_list list;
@@ -1414,7 +1126,6 @@
         }
     };
 
-    __attribute__((format(printf, 2, 3)))
     void logFuncVerbose(const char* format, ...) {
         if ( logPatchingVerbose ) {
             va_list list;
@@ -1600,7 +1311,7 @@
     DylibSymbols& kernelDylibSymbols = dylibsToSymbols[kernelID];
     SymbolLocation symbolLocation = findVTablePatchingSymbol("__ZTV11OSMetaClass", kernelDylibSymbols);
     if ( symbolLocation.found() ) {
-        baseMetaClassVTableLoc = (uint8_t*)kernelMA + (symbolLocation.vmAddr - ((const Header*)kernelMA)->preferredLoadAddress());
+        baseMetaClassVTableLoc = (uint8_t*)kernelMA + (symbolLocation.vmAddr - kernelMA->preferredLoadAddress());
 
         VTable& vtable = vtables[baseMetaClassVTableLoc];
         vtable.ma                   = kernelMA;
@@ -1642,10 +1353,10 @@
 
         // And add classic if we have them
         existingKernelCollection->forEachRebase(diags, ^(const char *opcodeName, const dyld3::MachOAnalyzer::LinkEditInfo &leInfo,
-                                                                const Header::SegmentInfo *segments,
+                                                                const dyld3::MachOAnalyzer::SegmentInfo *segments,
                                                                 bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex,
                                                                 uint64_t segmentOffset, dyld3::MachOAnalyzer::Rebase kind, bool &stop) {
-            uint64_t rebaseVmAddr  = segments[segmentIndex].vmaddr + segmentOffset;
+            uint64_t rebaseVmAddr  = segments[segmentIndex].vmAddr + segmentOffset;
             uint64_t runtimeOffset = rebaseVmAddr - kernelBaseAddress;
             const uint8_t* fixupLoc = kernelBasePointer + runtimeOffset;
             uint64_t targetVMAddr = 0;
@@ -1711,7 +1422,7 @@
 
     bool kernelUsesClassicRelocs = existingKernelCollection->usesClassicRelocationsInKernelCollection();
     existingKernelCollection->forEachDylib(diags, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
-        uint64_t loadAddress = ((const Header*)ma)->preferredLoadAddress();
+        uint64_t loadAddress = ma->preferredLoadAddress();
 
         auto visitBaseKernelCollectionSymbols = ^(const char *symbolName, uint64_t n_value) {
             if ( strstr(symbolName, superMetaclassPointerToken) == nullptr )
@@ -1747,7 +1458,7 @@
                 if ( symbolLocation.found() ) {
                     classVTableVMAddr = symbolLocation.vmAddr;
                 } else {
-                    diags.error("Class vtables '%s' or '%s' is not exported from '%s'",
+                    diags.error("Class vtables (%s) or (%s) is not exported from '%s'",
                                        classVTableName.c_str(), namespacedVTableName.c_str(), dylibID);
                     stop = true;
                     return;
@@ -1797,7 +1508,7 @@
             auto& metaclassDefinitions = collections[superclassFixupLevel].metaclassDefinitions;
             auto metaclassIt = metaclassDefinitions.find(superMetaclassSymbolAddress);
             if ( metaclassIt == metaclassDefinitions.end() ) {
-                diags.error("Cannot find symbol for metaclass pointed to by '%s' in '%s'",
+                diags.error("Cannot find symbol for metaclass pointed to by (%s) in '%s'",
                             symbolName, dylibID);
                 stop = true;
                 return;
@@ -1831,7 +1542,7 @@
                     for (const std::string& dependencyID : dependencies) {
                         auto depIt = dylibsToSymbols.find(dependencyID);
                         if (depIt == dylibsToSymbols.end()) {
-                            diags.error("Failed to bind '%s' in '%s' as could not find a kext with '%s' bundle-id",
+                            diags.error("Failed to bind (%s) in '%s' as could not find a kext with '%s' bundle-id",
                                         symbolName, dylibID, dependencyID.c_str());
                             stop = true;
                             return;
@@ -1851,7 +1562,7 @@
                 if ( superclassVTableLoc == nullptr ) {
                     auto depIt = dylibsToSymbols.find(dylibID);
                     if (depIt == dylibsToSymbols.end()) {
-                        diags.error("Failed to bind '%s' in '%s' as could not find a binary with '%s' bundle-id",
+                        diags.error("Failed to bind (%s) in '%s' as could not find a binary with '%s' bundle-id",
                                     symbolName, dylibID, dylibID);
                         stop = true;
                         return;
@@ -1872,7 +1583,7 @@
 
             if ( superclassVTableLoc == nullptr ) {
                 superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
-                diags.error("Superclass vtable '%s' is not exported from '%s' or its dependencies",
+                diags.error("Superclass vtable (%s) is not exported from '%s' or its dependencies",
                             superclassVTableName.c_str(), dylibID);
                 stop = true;
                 return;
@@ -1932,7 +1643,7 @@
     }
 
     pageableKernelCollection->forEachDylib(diags, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
-        uint64_t loadAddress = ((const Header*)ma)->preferredLoadAddress();
+        uint64_t loadAddress = ma->preferredLoadAddress();
         auto visitPageableKernelCollectionSymbols = ^(const char *symbolName, uint64_t n_value) {
             if ( strstr(symbolName, superMetaclassPointerToken) == nullptr )
                 return;
@@ -1967,7 +1678,7 @@
                 if ( symbolLocation.found() ) {
                     classVTableVMAddr = symbolLocation.vmAddr;
                 } else {
-                    diags.error("Class vtables '%s' or '%s' is not exported from '%s'",
+                    diags.error("Class vtables (%s0 or '%s' is not exported from '%s'",
                                 classVTableName.c_str(), namespacedVTableName.c_str(), dylibID);
                     stop = true;
                     return;
@@ -2012,7 +1723,7 @@
             auto& metaclassDefinitions = collections[superclassFixupLevel].metaclassDefinitions;
             auto metaclassIt = metaclassDefinitions.find(superMetaclassSymbolAddress);
             if ( metaclassIt == metaclassDefinitions.end() ) {
-                diags.error("Cannot find symbol for metaclass pointed to by '%s' in '%s'",
+                diags.error("Cannot find symbol for metaclass pointed to by (%s) in '%s'",
                             symbolName, dylibID);
                 stop = true;
                 return;
@@ -2046,7 +1757,7 @@
                     for (const std::string& dependencyID : dependencies) {
                         auto depIt = dylibsToSymbols.find(dependencyID);
                         if (depIt == dylibsToSymbols.end()) {
-                            diags.error("Failed to bind '%s' in '%s' as could not find a kext with '%s' bundle-id",
+                            diags.error("Failed to bind (%s) in '%s' as could not find a kext with '%s' bundle-id",
                                         symbolName, dylibID, dependencyID.c_str());
                             stop = true;
                             return;
@@ -2066,7 +1777,7 @@
                 if ( superclassVTableLoc == nullptr ) {
                     auto depIt = dylibsToSymbols.find(dylibID);
                     if (depIt == dylibsToSymbols.end()) {
-                        diags.error("Failed to bind '%s' in '%s' as could not find a binary with '%s' bundle-id",
+                        diags.error("Failed to bind (%s) in '%s' as could not find a binary with '%s' bundle-id",
                                     symbolName, dylibID, dylibID);
                         stop = true;
                         return;
@@ -2087,7 +1798,7 @@
 
             if ( superclassVTableLoc == nullptr ) {
                 superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
-                diags.error("Superclass vtable '%s' is not exported from '%s' or its dependencies",
+                diags.error("Superclass vtable (%s) is not exported from '%s' or its dependencies",
                             superclassVTableName.c_str(), dylibID);
                 stop = true;
                 return;
@@ -2133,7 +1844,7 @@
 
 void VTablePatcher::findVTables(uint8_t currentLevel, const dyld3::MachOAnalyzer* kernelMA,
                                 std::map<std::string, DylibSymbols>& dylibsToSymbols,
-                                const cache_builder::ASLR_Tracker& aslrTracker,
+                                const AppCacheBuilder::ASLR_Tracker& aslrTracker,
                                 const std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations)
 {
     const bool is64 = pointerSize == 8;
@@ -2165,7 +1876,7 @@
         Diagnostics&                dylibDiags          = *dylib.diags;
         const std::vector<std::string>& dependencies    = dylib.dependencies;
 
-        uint64_t loadAddress = ((const Header*)ma)->preferredLoadAddress();
+        uint64_t loadAddress = ma->preferredLoadAddress();
         bool alreadyPatched = (ma == kernelMA);
         auto visitSymbols = ^(const char *symbolName, uint64_t n_value) {
             if ( strstr(symbolName, superMetaclassPointerToken) == nullptr )
@@ -2202,7 +1913,7 @@
                 if ( symbolLocation.found() ) {
                     classVTableVMAddr = symbolLocation.vmAddr;
                 } else {
-                    dylibDiags.error("Class vtables '%s' or '%s' is not an exported symbol",
+                    dylibDiags.error("Class vtables (%s) or (%s) is not an exported symbol",
                                      classVTableName.c_str(), namespacedVTableName.c_str());
                     return;
                 }
@@ -2256,11 +1967,11 @@
             if ( metaclassIt == metaclassDefinitions.end() ) {
                 auto bindIt = missingBindLocations.find(fixupLoc);
                 if ( bindIt != missingBindLocations.end() ) {
-                    dylibDiags.error("Cannot find symbol for metaclass pointed to by '%s'.  "
-                                     "Expected symbol '%s' to be defined in another kext",
+                    dylibDiags.error("Cannot find symbol for metaclass pointed to by (%s).  "
+                                     "Expected symbol (%s) to be defined in another kext",
                                      symbolName, bindIt->second.symbolName.c_str());
                 } else {
-                    dylibDiags.error("Cannot find symbol for metaclass pointed to by '%s'",
+                    dylibDiags.error("Cannot find symbol for metaclass pointed to by (%s)",
                                      symbolName);
                 }
                 return;
@@ -2292,7 +2003,7 @@
                     for (const std::string& dependencyID : dependencies) {
                         auto depIt = dylibsToSymbols.find(dependencyID);
                         if (depIt == dylibsToSymbols.end()) {
-                            dylibDiags.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id",
+                            dylibDiags.error("Failed to bind (%s) as could not find a kext with '%s' bundle-id",
                                              symbolName, dependencyID.c_str());
                             return;
                         }
@@ -2325,7 +2036,7 @@
 
             if ( superclassVTableLoc == nullptr ) {
                 superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
-                dylibDiags.error("Superclass vtable '%s' is not exported from kext or its dependencies",
+                dylibDiags.error("Superclass vtable (%s) is not exported from kext or its dependencies",
                                  superclassVTableName.c_str());
                 return;
             }
@@ -2403,7 +2114,7 @@
 
 void VTablePatcher::patchVTables(Diagnostics& diags,
                                  std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations,
-                                 cache_builder::ASLR_Tracker& aslrTracker,
+                                 AppCacheBuilder::ASLR_Tracker& aslrTracker,
                                  uint8_t currentLevel)
 {
     const bool is64 = pointerSize == 8;
@@ -2475,7 +2186,7 @@
             }
         }
 
-        logFunc("Found %lu vtable items: '%s'\n", vtable.entries.size(), vtable.name.c_str());
+        logFunc("Found %d vtable items: '%s'\n", vtable.entries.size(), vtable.name.c_str());
     };
 
     // Map from MachO to diagnostics to emit for that file
@@ -2512,7 +2223,7 @@
                     continue;
                 auto superIt = vtables.find(vtableEntry.second.superVTable);
                 assert(superIt != vtables.end());
-                diags.error("Found unpatched vtable: '%s' with unpatched superclass '%s'\n",
+                diags.error("Found unpatched vtable: (%s) with unpatched superclass (%s)\n",
                             vtableEntry.second.name.c_str(), superIt->second.name.c_str());
             }
             break;
@@ -2678,7 +2389,7 @@
 struct DylibFixups {
     void processFixups(const std::map<std::string, DylibSymbols>& dylibsToSymbols,
                        const std::unordered_map<std::string_view, std::vector<DylibSymbolLocation>>& symbolMap,
-                       const std::string& kernelID, const cache_builder::ASLR_Tracker& aslrTracker);
+                       const std::string& kernelID, const CacheBuilder::ASLR_Tracker& aslrTracker);
 
     // Inputs
     const dyld3::MachOAnalyzer*     ma              = nullptr;
@@ -2706,7 +2417,7 @@
 
 void DylibFixups::processFixups(const std::map<std::string, DylibSymbols>& dylibsToSymbols,
                                 const std::unordered_map<std::string_view, std::vector<DylibSymbolLocation>>& symbolMap,
-                                const std::string& kernelID, const cache_builder::ASLR_Tracker& aslrTracker) {
+                                const std::string& kernelID, const CacheBuilder::ASLR_Tracker& aslrTracker) {
     auto& resolvedBindLocations = dylibSymbols.resolvedBindLocations;
     const std::string& dylibID = dylibSymbols.dylibName;
 
@@ -2775,7 +2486,7 @@
             for (const std::string& dependencyID : dependencies) {
                 auto depIt = dylibsToSymbols.find(dependencyID);
                 if (depIt == dylibsToSymbols.end()) {
-                    dylibDiag.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id",
+                    dylibDiag.error("Failed to bind (%s) as could not find a kext with '%s' bundle-id",
                                        symbolName, dependencyID.c_str());
                     stop = true;
                     return;
@@ -2796,7 +2507,7 @@
             if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
                 auto dylibIt = dylibsToSymbols.find(dylibID);
                 if (dylibIt == dylibsToSymbols.end()) {
-                    dylibDiag.error("Failed to bind weak '%s' as could not find a define in self",
+                    dylibDiag.error("Failed to bind weak (%s) as could not find a define in self",
                                     symbolName);
                     stop = true;
                     return;
@@ -2823,7 +2534,7 @@
                     bindTargets.push_back({ bindSymbol, exportIt->second, kernelSymbols.dylibLevel, true, isMissingSymbol });
                     return;
                 }
-                dylibDiag.error("Weak bind symbol '%s' not found in kernel", missingWeakImportSymbolName);
+                dylibDiag.error("Weak bind symbol (%s) not found in kernel", missingWeakImportSymbolName);
                 return;
             }
 
@@ -2861,6 +2572,8 @@
                 return;
             }
         }
+
+        // uint64_t baseAddress = ma->preferredLoadAddress();
 
         ma->withChainStarts(dylibDiag, 0, ^(const dyld_chained_starts_in_image* starts) {
             ma->forEachFixupInAllChains(dylibDiag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
@@ -2972,7 +2685,7 @@
     __block bool foundUseOfMagicSymbol = false;
     __block bool foundMissingWeakImport = false;
 
-    const uint64_t loadAddress = ((const Header*)ma)->preferredLoadAddress();
+    const uint64_t loadAddress = ma->preferredLoadAddress();
     ma->forEachBind(dylibDiag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t bindType,
                                  const char *symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool &stop) {
         // printf("Bind at 0x%llx to '%s'\n", runtimeOffset, symbolName);
@@ -3025,7 +2738,7 @@
             for (const std::string& dependencyID : dependencies) {
                 auto depIt = dylibsToSymbols.find(dependencyID);
                 if (depIt == dylibsToSymbols.end()) {
-                    dylibDiag.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id",
+                    dylibDiag.error("Failed to bind (%s) as could not find a kext with '%s' bundle-id",
                                     symbolName, dependencyID.c_str());
                     stop = true;
                     return;
@@ -3045,7 +2758,7 @@
         if ( !foundSymbol && (libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP) ) {
             auto dylibIt = dylibsToSymbols.find(dylibID);
             if (dylibIt == dylibsToSymbols.end()) {
-                dylibDiag.error("Failed to bind weak '%s' as could not find a define in self",
+                dylibDiag.error("Failed to bind weak (%s) as could not find a define in self",
                                 symbolName);
                 stop = true;
                 return;
@@ -3105,7 +2818,7 @@
                 fixupLocs[fixupLoc] = kernelSymbols.dylibLevel;
                 return;
             }
-            dylibDiag.error("Weak bind symbol '%s' not found in kernel", missingWeakImportSymbolName);
+            dylibDiag.error("Weak bind symbol (%s) not found in kernel", missingWeakImportSymbolName);
             return;
         }
 
@@ -3245,152 +2958,13 @@
     return std::make_unique<std::unordered_set<std::string>>(std::move(symbols));
 }
 
-void AppCacheBuilder::patchVTables(const dyld3::MachOAnalyzer* kernelMA,
-                                   const std::string& kernelID,
-                                   std::map<std::string, DylibSymbols>& dylibsToSymbols,
-                                   std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations)
-{
-    // We only patch vtables on macOS.  Luckily the platform is in the kernel binary
-    if ( !((mach_o::Header*)kernelMA)->builtForPlatform(mach_o::Platform::macOS) )
-        return;
-
-    auto vtablePatcherOwner = std::make_unique<VTablePatcher>(numFixupLevels);
-    VTablePatcher& vtablePatcher = *vtablePatcherOwner.get();
-
-    uint8_t currentLevel = getCurrentFixupLevel();
-
-    // Add all the collections to the vtable patcher
-    if ( existingKernelCollection != nullptr ) {
-        // The baseKC for x86_64 has __HIB mapped first , so we need to get either the __DATA or __TEXT depending on what is earliest
-        // The kernel base address is still __TEXT, even if __DATA or __HIB is mapped prior to that.
-        // The loader may have loaded something before __TEXT, but the existingKernelCollection pointer still corresponds to __TEXT
-        __block uint64_t baseAddress = ~0ULL;
-        ((const Header*)existingKernelCollection)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-            baseAddress = std::min(baseAddress, info.vmaddr);
-        });
-
-        // The existing collection is a pointer to the mach_header for the baseKC, but __HIB and other segments may be before that
-        // Offset those here
-        uint64_t basePointerOffset = ((const Header*)existingKernelCollection)->preferredLoadAddress() - baseAddress;
-        const uint8_t* basePointer = (uint8_t*)existingKernelCollection - basePointerOffset;
-
-        vtablePatcher.addKernelCollection(existingKernelCollection, Options::AppCacheKind::kernel,
-                                          basePointer, baseAddress);
-    }
-
-    if ( pageableKernelCollection != nullptr ) {
-        // The baseKC for x86_64 has __HIB mapped first , so we need to get either the __DATA or __TEXT depending on what is earliest
-        // The kernel base address is still __TEXT, even if __DATA or __HIB is mapped prior to that.
-        // The loader may have loaded something before __TEXT, but the existingKernelCollection pointer still corresponds to __TEXT
-        __block uint64_t baseAddress = ~0ULL;
-        ((const Header*)pageableKernelCollection)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-            baseAddress = std::min(baseAddress, info.vmaddr);
-        });
-
-        // The existing collection is a pointer to the mach_header for the baseKC, but __HIB and other segments may be before that
-        // Offset those here
-        uint64_t basePointerOffset = ((const Header*)pageableKernelCollection)->preferredLoadAddress() - baseAddress;
-        const uint8_t* basePointer = (uint8_t*)pageableKernelCollection - basePointerOffset;
-
-        vtablePatcher.addKernelCollection(pageableKernelCollection, Options::AppCacheKind::pageableKC,
-                                          basePointer, baseAddress);
-    }
-
-    // Also add our KC
-    vtablePatcher.addKernelCollection((const dyld3::MachOAppCache*)cacheHeader.header, appCacheOptions.cacheKind,
-                                      (const uint8_t*)_fullAllocatedBuffer, cacheBaseAddress);
-
-    // Add all the dylibs to the patcher
-    {
-        if ( existingKernelCollection != nullptr ) {
-            uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::kernel);
-
-            __block std::map<std::string, std::vector<std::string>> kextDependencies;
-            kextDependencies[kernelID] = {};
-            existingKernelCollection->forEachPrelinkInfoLibrary(_diagnostics,
-                                                                ^(const char *bundleName, const char* relativePath,
-                                                                  const std::vector<const char *> &deps) {
-                std::vector<std::string>& dependencies = kextDependencies[bundleName];
-                dependencies.insert(dependencies.end(), deps.begin(), deps.end());
-            });
-
-            existingKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
-                auto depsIt = kextDependencies.find(dylibID);
-                assert(depsIt != kextDependencies.end());
-                vtablePatcher.addDylib(_diagnostics, ma, dylibID, depsIt->second, fixupLevel);
-            });
-        }
-
-        if ( pageableKernelCollection != nullptr ) {
-            uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC);
-
-            __block std::map<std::string, std::vector<std::string>> kextDependencies;
-            pageableKernelCollection->forEachPrelinkInfoLibrary(_diagnostics,
-                                                                ^(const char *bundleName, const char* relativePath,
-                                                                  const std::vector<const char *> &deps) {
-                std::vector<std::string>& dependencies = kextDependencies[bundleName];
-                dependencies.insert(dependencies.end(), deps.begin(), deps.end());
-            });
-
-            pageableKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
-                auto depsIt = kextDependencies.find(dylibID);
-                assert(depsIt != kextDependencies.end());
-                vtablePatcher.addDylib(_diagnostics, ma, dylibID, depsIt->second, fixupLevel);
-            });
-        }
-
-        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode,
-                            const std::vector<std::string> &dependencies, Diagnostics& dylibDiag, bool &stop) {
-            vtablePatcher.addDylib(dylibDiag, ma, dylibID, dependencies, currentLevel);
-        });
-    }
-
-    vtablePatcher.findMetaclassDefinitions(dylibsToSymbols, kernelID, kernelMA, appCacheOptions.cacheKind);
-    vtablePatcher.findExistingFixups(_diagnostics, existingKernelCollection, pageableKernelCollection);
-    if ( _diagnostics.hasError() )
-        return;
-
-    // Add vtables from the base KC if we have one
-    if ( existingKernelCollection != nullptr ) {
-        vtablePatcher.findBaseKernelVTables(_diagnostics, existingKernelCollection, dylibsToSymbols);
-        if ( _diagnostics.hasError() )
-            return;
-    }
-
-    // Add vtables from the pageable KC if we have one
-    if ( pageableKernelCollection != nullptr ) {
-        vtablePatcher.findPageableKernelVTables(_diagnostics, pageableKernelCollection, dylibsToSymbols);
-        if ( _diagnostics.hasError() )
-            return;
-    }
-
-    // Add vables from our level
-    vtablePatcher.findVTables(currentLevel, kernelMA, dylibsToSymbols, _aslrTracker, missingBindLocations);
-
-    // Don't run the patcher if we have a failure finding the vtables
-    if ( vtablePatcher.hasError() ) {
-        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
-        return;
-    }
-
-    // Now patch all of the vtables.
-    vtablePatcher.patchVTables(_diagnostics, missingBindLocations, _aslrTracker, currentLevel);
-    if ( _diagnostics.hasError() )
-        return;
-
-    if ( vtablePatcher.hasError() ) {
-        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
-        return;
-    }
-
-    // FIXME: We could move vtablePatcherOwner to a worker thread to be destroyed
-    vtablePatcherOwner.reset();
-}
-
 void AppCacheBuilder::processFixups()
 {
     auto dylibsToSymbolsOwner = std::make_unique<std::map<std::string, DylibSymbols>>();
     std::map<std::string, DylibSymbols>& dylibsToSymbols = *dylibsToSymbolsOwner.get();
+
+    auto vtablePatcherOwner = std::make_unique<VTablePatcher>(numFixupLevels);
+    VTablePatcher& vtablePatcher = *vtablePatcherOwner.get();
 
     const uint32_t kernelLevel = 0;
     uint8_t currentLevel = getCurrentFixupLevel();
@@ -3640,7 +3214,7 @@
                         if (globalIt != kernelSymbols.globals.end()) {
                             symbolSetGlobals[symbolName] = globalIt->second;
                         } else {
-                            _diagnostics.error("Alias '%s' not found in kernel", aliasTargetName);
+                            _diagnostics.error("Alias (%s0 not found in kernel", aliasTargetName);
                             return;
                         }
 
@@ -3829,7 +3403,7 @@
             }
 
             // Emit branch stubs
-            const uint64_t loadAddress = ((const Header*)dylibFixup.ma)->preferredLoadAddress();
+            const uint64_t loadAddress = dylibFixup.ma->preferredLoadAddress();
             for (const DylibFixups::BranchStubData& branchData : dylibFixup.branchStubs) {
                 // Branching from the auxKC to baseKC.  ld64 doesn't emit a stub in x86_64 kexts
                 // so we need to synthesize one now
@@ -3884,9 +3458,133 @@
     }
 
     // Now that we've processes all rebases/binds, patch all the vtables
-    this->patchVTables(kernelMA, kernelID, dylibsToSymbols, missingBindLocations);
+
+    // Add all the collections to the vtable patcher
+    if ( existingKernelCollection != nullptr ) {
+        // The baseKC for x86_64 has __HIB mapped first , so we need to get either the __DATA or __TEXT depending on what is earliest
+        // The kernel base address is still __TEXT, even if __DATA or __HIB is mapped prior to that.
+        // The loader may have loaded something before __TEXT, but the existingKernelCollection pointer still corresponds to __TEXT
+        __block uint64_t baseAddress = ~0ULL;
+        existingKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
+        });
+
+        // The existing collection is a pointer to the mach_header for the baseKC, but __HIB and other segments may be before that
+        // Offset those here
+        uint64_t basePointerOffset = existingKernelCollection->preferredLoadAddress() - baseAddress;
+        const uint8_t* basePointer = (uint8_t*)existingKernelCollection - basePointerOffset;
+
+        vtablePatcher.addKernelCollection(existingKernelCollection, Options::AppCacheKind::kernel,
+                                          basePointer, baseAddress);
+    }
+
+    if ( pageableKernelCollection != nullptr ) {
+        // The baseKC for x86_64 has __HIB mapped first , so we need to get either the __DATA or __TEXT depending on what is earliest
+        // The kernel base address is still __TEXT, even if __DATA or __HIB is mapped prior to that.
+        // The loader may have loaded something before __TEXT, but the existingKernelCollection pointer still corresponds to __TEXT
+        __block uint64_t baseAddress = ~0ULL;
+        pageableKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
+        });
+
+        // The existing collection is a pointer to the mach_header for the baseKC, but __HIB and other segments may be before that
+        // Offset those here
+        uint64_t basePointerOffset = pageableKernelCollection->preferredLoadAddress() - baseAddress;
+        const uint8_t* basePointer = (uint8_t*)pageableKernelCollection - basePointerOffset;
+
+        vtablePatcher.addKernelCollection(pageableKernelCollection, Options::AppCacheKind::pageableKC,
+                                          basePointer, baseAddress);
+    }
+
+    // Also add our KC
+    vtablePatcher.addKernelCollection((const dyld3::MachOAppCache*)cacheHeader.header, appCacheOptions.cacheKind,
+                                      (const uint8_t*)_fullAllocatedBuffer, cacheBaseAddress);
+
+    // Add all the dylibs to the patcher
+    {
+        if ( existingKernelCollection != nullptr ) {
+            uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::kernel);
+
+            __block std::map<std::string, std::vector<std::string>> kextDependencies;
+            kextDependencies[kernelID] = {};
+            existingKernelCollection->forEachPrelinkInfoLibrary(_diagnostics,
+                                                                ^(const char *bundleName, const char* relativePath,
+                                                                  const std::vector<const char *> &deps) {
+                std::vector<std::string>& dependencies = kextDependencies[bundleName];
+                dependencies.insert(dependencies.end(), deps.begin(), deps.end());
+            });
+
+            existingKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
+                auto depsIt = kextDependencies.find(dylibID);
+                assert(depsIt != kextDependencies.end());
+                vtablePatcher.addDylib(_diagnostics, ma, dylibID, depsIt->second, fixupLevel);
+            });
+        }
+
+        if ( pageableKernelCollection != nullptr ) {
+            uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC);
+
+            __block std::map<std::string, std::vector<std::string>> kextDependencies;
+            pageableKernelCollection->forEachPrelinkInfoLibrary(_diagnostics,
+                                                                ^(const char *bundleName, const char* relativePath,
+                                                                  const std::vector<const char *> &deps) {
+                std::vector<std::string>& dependencies = kextDependencies[bundleName];
+                dependencies.insert(dependencies.end(), deps.begin(), deps.end());
+            });
+
+            pageableKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
+                auto depsIt = kextDependencies.find(dylibID);
+                assert(depsIt != kextDependencies.end());
+                vtablePatcher.addDylib(_diagnostics, ma, dylibID, depsIt->second, fixupLevel);
+            });
+        }
+
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode,
+                            const std::vector<std::string> &dependencies, Diagnostics& dylibDiag, bool &stop) {
+            vtablePatcher.addDylib(dylibDiag, ma, dylibID, dependencies, currentLevel);
+        });
+    }
+
+    vtablePatcher.findMetaclassDefinitions(dylibsToSymbols, kernelID, kernelMA, appCacheOptions.cacheKind);
+    vtablePatcher.findExistingFixups(_diagnostics, existingKernelCollection, pageableKernelCollection);
     if ( _diagnostics.hasError() )
         return;
+
+    // Add vtables from the base KC if we have one
+    if ( existingKernelCollection != nullptr ) {
+        vtablePatcher.findBaseKernelVTables(_diagnostics, existingKernelCollection, dylibsToSymbols);
+        if ( _diagnostics.hasError() )
+            return;
+    }
+
+    // Add vtables from the pageable KC if we have one
+    if ( pageableKernelCollection != nullptr ) {
+        vtablePatcher.findPageableKernelVTables(_diagnostics, pageableKernelCollection, dylibsToSymbols);
+        if ( _diagnostics.hasError() )
+            return;
+    }
+
+    // Add vables from our level
+    vtablePatcher.findVTables(currentLevel, kernelMA, dylibsToSymbols, _aslrTracker, missingBindLocations);
+
+    // Don't run the patcher if we have a failure finding the vtables
+    if ( vtablePatcher.hasError() ) {
+        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
+        return;
+    }
+
+    // Now patch all of the vtables.
+    vtablePatcher.patchVTables(_diagnostics, missingBindLocations, _aslrTracker, currentLevel);
+    if ( _diagnostics.hasError() )
+        return;
+
+    if ( vtablePatcher.hasError() ) {
+        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
+        return;
+    }
+
+    // FIXME: We could move vtablePatcherOwner to a worker thread to be destroyed
+    vtablePatcherOwner.reset();
 
     // Also error out if we have an error on any of the dylib diagnostic objects
 
@@ -3900,20 +3598,20 @@
         forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode,
                             const std::vector<std::string> &dependencies, Diagnostics& dylibDiag, bool &stopDylib) {
             intptr_t slide = ma->getSlide();
-            ((const Header*)ma)->forEachSection(^(const Header::SectionInfo &sectInfo,
-                                 bool &stopSection) {
-                const uint8_t* content  = (uint8_t*)(sectInfo.address + slide);
+            ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo,
+                                 bool malformedSectionRange, bool &stopSection) {
+                const uint8_t* content  = (uint8_t*)(sectInfo.sectAddr + slide);
                 const uint8_t* start    = (uint8_t*)content;
-                const uint8_t* end      = start + sectInfo.size;
+                const uint8_t* end      = start + sectInfo.sectSize;
                 if ( (missingBindLoc >= start) && (missingBindLoc < end) ) {
+                    std::string segmentName = sectInfo.segInfo.segName;
+                    std::string sectionName = sectInfo.sectName;
                     uint64_t sectionOffset = (missingBindLoc - start);
-                    
-                    dylibDiag.error("Failed to bind '%s' in '%s' (at offset 0x%llx in %.*s, %.*s) as "
+
+                    dylibDiag.error("Failed to bind (%s) in '%s' (at offset 0x%llx in %s, %s) as "
                                     "could not find a kext which exports this symbol",
                                     missingBind.symbolName.c_str(), missingBind.binaryID.data(),
-                                    sectionOffset,
-                                    (int)sectInfo.segmentName.size(), sectInfo.segmentName.data(),
-                                    (int)sectInfo.sectionName.size(), sectInfo.sectionName.data());
+                                    sectionOffset, segmentName.c_str(), sectionName.c_str());
 
                     reportedError = true;
                     stopSection = true;
@@ -3923,7 +3621,7 @@
         });
 
         if ( !reportedError ) {
-            _diagnostics.error("Failed to bind '%s' in '%s' as could not find a kext which exports this symbol",
+            _diagnostics.error("Failed to bind (%s) in '%s' as could not find a kext which exports this symbol",
                                missingBind.symbolName.c_str(), missingBind.binaryID.data());
         }
     }
@@ -3989,23 +3687,23 @@
     if ( header.dynSymbolTable != nullptr ) {
         classicRelocsBufferStart = byteBuffer.begin();
 
-        const Header* cacheMH = (const Header*)header.header;
+        dyld3::MachOAnalyzer* cacheMA = (dyld3::MachOAnalyzer*)header.header;
         __block uint64_t localRelocBaseAddress = 0;
-        cacheMH->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
-            if ( info.initProt & VM_PROT_WRITE ) {
-                localRelocBaseAddress = info.vmaddr;
+        cacheMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+            if ( info.protections & VM_PROT_WRITE ) {
+                localRelocBaseAddress = info.vmAddr;
                 stop = true;
             }
         });
 
         const std::vector<void*> allRebaseTargets = _aslrTracker.getRebaseTargets();
 
-        const Header* kernelMH = (const Header*)getKernelStaticExecutableFromCache();
-        kernelMH->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
+        const dyld3::MachOAnalyzer* kernelMA = getKernelStaticExecutableFromCache();
+        kernelMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
             std::vector<void*> segmentRebaseTargets;
-            uint64_t segmentVMOffset = info.vmaddr - cacheBaseAddress;
+            uint64_t segmentVMOffset = info.vmAddr - cacheBaseAddress;
             const uint8_t* segmentStartAddr = (const uint8_t*)(_fullAllocatedBuffer + segmentVMOffset);
-            const uint8_t* segmentEndAddr = (const uint8_t*)(segmentStartAddr + info.vmsize);
+            const uint8_t* segmentEndAddr = (const uint8_t*)(segmentStartAddr + info.vmSize);
             for (void* target : allRebaseTargets) {
                 if ( (target >= segmentStartAddr) && (target < segmentEndAddr) ) {
                     segmentRebaseTargets.push_back(target);
@@ -4017,7 +3715,7 @@
                 uint64_t targetSegmentOffset = (uint64_t)target - (uint64_t)segmentStartAddr;
                 //printf("Target: %s + 0x%llx: %p\n", info.segName, targetSegmentOffset, target);
 
-                uint64_t offsetFromBaseAddress = (info.vmaddr + targetSegmentOffset) - localRelocBaseAddress;
+                uint64_t offsetFromBaseAddress = (info.vmAddr + targetSegmentOffset) - localRelocBaseAddress;
                 relocation_info* reloc = (relocation_info*)byteBuffer.makeSpace(sizeof(relocation_info));
                 reloc->r_address = (uint32_t)offsetFromBaseAddress;
                 reloc->r_symbolnum = 0;
@@ -4060,8 +3758,8 @@
         assert(existingKernelCollection != nullptr);
         // The auxKC is mapped with __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest
         __block uint64_t baseAddress = ~0ULL;
-        ((const Header*)existingKernelCollection)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-            baseAddress = std::min(baseAddress, info.vmaddr);
+        existingKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
         });
         levelBaseAddresses[0] = baseAddress;
     }
@@ -4069,8 +3767,8 @@
     if ( pageableKernelCollection != nullptr ) {
         // We may have __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest
         __block uint64_t baseAddress = ~0ULL;
-        ((const Header *)pageableKernelCollection)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-            baseAddress = std::min(baseAddress, info.vmaddr);
+        pageableKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
         });
         uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC);
         levelBaseAddresses[fixupLevel] = baseAddress;
@@ -4101,9 +3799,11 @@
         // We have a dyld_chained_starts_in_image plus an offset for each segment
         dyld_chained_starts_in_image* startsInImage = (dyld_chained_starts_in_image*)byteBuffer.makeSpace(sizeof(dyld_chained_starts_in_image) + (segmentCount * sizeof(uint32_t)));
 
+        const uint8_t* endOfStarts = nullptr;
         for (SegmentFixups& segmentFixups : startsInSegments) {
             uint64_t startsInSegmentByteSize = sizeof(dyld_chained_starts_in_segment) + (segmentFixups.numPagesToFixup * sizeof(uint16_t));
             dyld_chained_starts_in_segment* startsInSegment = (dyld_chained_starts_in_segment*)byteBuffer.makeSpace(startsInSegmentByteSize);
+            endOfStarts = (const uint8_t*)startsInSegment + startsInSegmentByteSize;
 
             segmentFixups.starts            = startsInSegment;
             segmentFixups.startsByteSize    = startsInSegmentByteSize;
@@ -4217,30 +3917,30 @@
         forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
                             DylibStripMode stripMode, const std::vector<std::string> &dependencies,
                             Diagnostics& dylibDiag, bool &stop) {
-            uint64_t loadAddress = ((const Header*)ma)->preferredLoadAddress();
+            uint64_t loadAddress = ma->preferredLoadAddress();
 
             __block uint64_t                    numSegments = 0;
             __block std::vector<SegmentFixups>  segmentFixups;
-            ((const Header*)ma)->forEachSegment(^(const Header::SegmentInfo &info, bool &stopSegments) {
+            ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stopSegments) {
                 // Third party kexts have writable __TEXT, so we need to add starts for all segments
                 // other than LINKEDIT
                 bool segmentCanHaveFixups = false;
                 if ( appCacheOptions.cacheKind == Options::AppCacheKind::pageableKC ) {
-                    segmentCanHaveFixups = (info.initProt & VM_PROT_WRITE) != 0;
+                    segmentCanHaveFixups = (info.protections & VM_PROT_WRITE) != 0;
                 } else {
                     // auxKC
-                    segmentCanHaveFixups = info.segmentName != "__LINKEDIT";
+                    segmentCanHaveFixups = (strcmp(info.segName, "__LINKEDIT") != 0);
                 }
 
                 if ( segmentCanHaveFixups) {
                     SegmentFixups segmentToFixup;
-                    segmentToFixup.segmentBuffer        = (uint8_t*)ma + (info.vmaddr - loadAddress);
-                    segmentToFixup.segmentIndex         = info.segmentIndex;
-                    segmentToFixup.unslidLoadAddress    = info.vmaddr;
-                    segmentToFixup.sizeInUse            = info.vmsize;
+                    segmentToFixup.segmentBuffer        = (uint8_t*)ma + (info.vmAddr - loadAddress);
+                    segmentToFixup.segmentIndex         = info.segIndex;
+                    segmentToFixup.unslidLoadAddress    = info.vmAddr;
+                    segmentToFixup.sizeInUse            = info.vmSize;
                     segmentToFixup.starts               = nullptr;
                     segmentToFixup.startsByteSize       = 0;
-                    segmentToFixup.numPagesToFixup      = numWritablePagesToFixup(info.vmsize);
+                    segmentToFixup.numPagesToFixup      = numWritablePagesToFixup(info.vmSize);
                     segmentFixups.push_back(segmentToFixup);
                 }
 
@@ -4263,7 +3963,7 @@
                 assert(_is64);
                 typedef Pointer64<LittleEndian> P;
 
-                uint32_t freeSpace = ((const Header*)ma)->loadCommandsFreeSpace();
+                uint32_t freeSpace = ma->loadCommandsFreeSpace();
                 assert(freeSpace >= sizeof(macho_linkedit_data_command<P>));
                 uint8_t* endOfLoadCommands = (uint8_t*)ma + sizeof(macho_header<P>) + ma->sizeofcmds;
 
@@ -4329,10 +4029,6 @@
 
         if ( dataConstRegion.sizeInUse != 0 )
             addSegmentStarts(dataConstRegion);
-        if ( lateConstRegion.sizeInUse != 0 )
-            addSegmentStarts(lateConstRegion);
-        if ( dataSptmRegion.sizeInUse != 0 )
-            addSegmentStarts(dataSptmRegion);
         if ( branchGOTsRegion.sizeInUse != 0 )
             addSegmentStarts(branchGOTsRegion);
         if ( readWriteRegion.sizeInUse != 0 )
@@ -4392,157 +4088,6 @@
 #endif
 }
 
-void AppCacheBuilder::getRegionOrder(bool dataRegionFirstInVMOrder,
-                                     bool hibernateRegionFirstInVMOrder,
-                                     std::vector<AlignedRegion>& fileOrder,
-                                     std::vector<AlignedRegion>& vmOrder,
-                                     std::map<const Region*, uint32_t>& sectionsToAddToRegions)
-{
-    if ( hibernateRegionFirstInVMOrder ) {
-        vmOrder.emplace_back(&hibernateRegion, 14, 14);
-        // Add a section too
-        sectionsToAddToRegions[&hibernateRegion] = 1;
-    } else if ( dataRegionFirstInVMOrder ) {
-        if ( prelinkInfoDict != nullptr ) {
-            vmOrder.emplace_back(&prelinkInfoRegion, 14, 14);
-        }
-        if ( readWriteRegion.sizeInUse != 0 ) {
-            vmOrder.emplace_back(&readWriteRegion, 14, 14);
-        }
-    }
-
-    // Cache header (__TEXT)
-    vmOrder.emplace_back(&cacheHeaderRegion, 14, 14);
-    fileOrder.emplace_back(&cacheHeaderRegion, 14, 14);
-
-    // Split seg __TEXT (ie, __PRELINK_TEXT)
-    {
-        vmOrder.emplace_back(&readOnlyTextRegion, 14, 14);
-        fileOrder.emplace_back(&readOnlyTextRegion, 14, 14);
-        // Add a section too
-        sectionsToAddToRegions[&readOnlyTextRegion] = 1;
-    }
-
-    // -sectcreate
-    // Align to 16k before we lay out all contiguous regions
-    if ( !customSegments.empty() ) {
-        uint32_t alignFileBefore = 14;
-        for (CustomSegment& customSegment : customSegments) {
-            Region& region = *customSegment.parentRegion;
-            vmOrder.emplace_back(&region, 0, 0);
-            fileOrder.emplace_back(&region, alignFileBefore, 0);
-            alignFileBefore = 0;
-
-            // Maybe add sections too
-            uint32_t sectionsToAdd = 0;
-            if ( customSegment.sections.size() > 1 ) {
-                // More than one section, so they all need names
-                sectionsToAdd = (uint32_t)customSegment.sections.size();
-            } else if ( !customSegment.sections.front().sectionName.empty() ) {
-                // Only one section, but it has a name
-                sectionsToAdd = 1;
-            }
-            sectionsToAddToRegions[&region] = sectionsToAdd;
-        }
-
-        // Align the last region after
-        vmOrder.back().alignmentAfter = 14;
-        fileOrder.back().alignmentAfter = 14;
-    }
-
-    // __DATA_CONST
-    if ( dataConstRegion.sizeInUse != 0 ) {
-        vmOrder.emplace_back(&dataConstRegion, 14, 14);
-        fileOrder.emplace_back(&dataConstRegion, 14, 14);
-    }
-
-    // __LATE_CONST
-    if ( lateConstRegion.sizeInUse != 0 ) {
-        vmOrder.emplace_back(&lateConstRegion, 14, 14);
-        fileOrder.emplace_back(&lateConstRegion, 14, 14);
-    }
-
-    // __DATA_SPTM
-    if ( dataSptmRegion.sizeInUse != 0 ) {
-        vmOrder.emplace_back(&dataSptmRegion, 14, 14);
-        fileOrder.emplace_back(&dataSptmRegion, 14, 14);
-    }
-
-    // Split seg __TEXT_EXEC
-    if ( readExecuteRegion.sizeInUse != 0 ) {
-        vmOrder.emplace_back(&readExecuteRegion, 14, 14);
-        fileOrder.emplace_back(&readExecuteRegion, 14, 14);
-    }
-
-    // __BRANCH_STUBS
-    if ( branchStubsRegion.bufferSize != 0 ) {
-        vmOrder.emplace_back(&branchStubsRegion, 14, 14);
-        fileOrder.emplace_back(&branchStubsRegion, 14, 14);
-    }
-
-    // __TEXT_BOOT_EXEC
-    if ( textBootExecRegion.sizeInUse != 0 ) {
-        vmOrder.emplace_back(&textBootExecRegion, 14, 14);
-        fileOrder.emplace_back(&textBootExecRegion, 14, 14);
-    }
-
-    // __BRANCH_GOTS
-    if ( branchGOTsRegion.bufferSize != 0 ) {
-        vmOrder.emplace_back(&branchGOTsRegion, 14, 14);
-        fileOrder.emplace_back(&branchGOTsRegion, 14, 14);
-    }
-
-    // __PRELINK_INFO
-    if ( prelinkInfoDict != nullptr )
-    {
-        fileOrder.emplace_back(&prelinkInfoRegion, 14, 14);
-        if ( !dataRegionFirstInVMOrder )
-            vmOrder.emplace_back(&prelinkInfoRegion, 14, 14);
-        // Add a section too
-        sectionsToAddToRegions[&prelinkInfoRegion] = 1;
-    }
-
-    // Split seg __DATA
-    if ( readWriteRegion.sizeInUse != 0 ) {
-        fileOrder.emplace_back(&readWriteRegion, 14, 14);
-        if ( !dataRegionFirstInVMOrder ) {
-            vmOrder.emplace_back(&readWriteRegion, 14, 14);
-        }
-    }
-
-    // Split seg __HIB
-    // Align to 16k
-    if ( hibernateRegion.sizeInUse != 0 ) {
-        fileOrder.emplace_back(&hibernateRegion, 14, 14);
-        // VM offset was already handled earlier
-    }
-
-    // Non split seg regions
-    // Align to 16k before we lay out all contiguous regions
-    if ( !nonSplitSegRegions.empty() ) {
-        uint32_t alignFileBefore = 14;
-        for (Region& region : nonSplitSegRegions) {
-            vmOrder.emplace_back(&region, 0, 0);
-            fileOrder.emplace_back(&region, alignFileBefore, 0);
-            alignFileBefore = 0;
-        }
-
-        // Align the last region after
-        vmOrder.back().alignmentAfter = 14;
-        fileOrder.back().alignmentAfter = 14;
-    }
-
-    // __LINKEDIT
-    vmOrder.emplace_back(&_readOnlyRegion, 14, 14);
-    fileOrder.emplace_back(&_readOnlyRegion, 14, 14);
-
-    // __LINKEDIT fixups sub region
-    if ( fixupsSubRegion.sizeInUse != 0 ) {
-        vmOrder.emplace_back(&fixupsSubRegion, 14, 14);
-        fileOrder.emplace_back(&fixupsSubRegion, 14, 14);
-    }
-}
-
 void AppCacheBuilder::allocateBuffer()
 {
     // Whether to order the regions __TEXT, __DATA, __LINKEDIT or __DATA, __TEXT, __LINKEDIT in VM address order
@@ -4553,7 +4098,7 @@
             assert(0 && "Cache kind should have been set");
             break;
         case Options::AppCacheKind::kernel:
-            if ( hibernateRegion.sizeInUse != 0 )
+            if ( hibernateAddress != 0 )
                 hibernateRegionFirstInVMOrder = true;
             break;
         case Options::AppCacheKind::pageableKC:
@@ -4567,15 +4112,232 @@
             break;
     }
 
-    std::vector<AlignedRegion> fileOrder;
-    std::vector<AlignedRegion> vmOrder;
+    // Count how many bytes we need from all our regions
+    __block uint64_t numRegionFileBytes = 0;
+    __block uint64_t numRegionVMBytes = 0;
+
+    std::vector<std::pair<Region*, uint64_t>> regions;
+    std::vector<std::pair<Region*, uint64_t>> regionsVMOrder;
     std::map<const Region*, uint32_t> sectionsToAddToRegions;
-    getRegionOrder(dataRegionFirstInVMOrder, hibernateRegionFirstInVMOrder,
-                   fileOrder, vmOrder, sectionsToAddToRegions);
+
+    if ( hibernateRegionFirstInVMOrder ) {
+        regionsVMOrder.push_back({ &hibernateRegion, numRegionVMBytes });
+        // Pad out the VM offset so that the cache header starts where the base address
+        // really should be
+        uint64_t paddedSize = cacheBaseAddress - hibernateAddress;
+        if ( hibernateRegion.bufferSize > paddedSize ) {
+            _diagnostics.error("Could not lay out __HIB segment");
+            return;
+        }
+        numRegionVMBytes = paddedSize;
+        // Set the base address to the hibernate address so that we actually put the
+        // hibernate segment there
+        cacheBaseAddress = hibernateAddress;
+
+        // Add a section too
+        sectionsToAddToRegions[&hibernateRegion] = 1;
+    } else if ( dataRegionFirstInVMOrder ) {
+        if ( prelinkInfoDict != nullptr ) {
+            numRegionVMBytes = align(numRegionVMBytes, 14);
+            regionsVMOrder.push_back({ &prelinkInfoRegion, numRegionVMBytes });
+            numRegionVMBytes += prelinkInfoRegion.bufferSize;
+        }
+        if ( readWriteRegion.sizeInUse != 0 ) {
+            numRegionVMBytes = align(numRegionVMBytes, 14);
+            regionsVMOrder.push_back({ &readWriteRegion, numRegionVMBytes });
+            numRegionVMBytes += readWriteRegion.bufferSize;
+        }
+    }
+
+    // Cache header
+    numRegionVMBytes = align(numRegionVMBytes, 14);
+    regions.push_back({ &cacheHeaderRegion, 0 });
+    regionsVMOrder.push_back({ &cacheHeaderRegion, numRegionVMBytes });
+
+    // Split seg __TEXT
+    {
+        // File offset
+        readOnlyTextRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += readOnlyTextRegion.bufferSize;
+        regions.push_back({ &readOnlyTextRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &readOnlyTextRegion, numRegionVMBytes });
+        numRegionVMBytes += readOnlyTextRegion.bufferSize;
+
+        // Add a section too
+        sectionsToAddToRegions[&readOnlyTextRegion] = 1;
+    }
+
+    // Split seg __TEXT_EXEC
+    if ( readExecuteRegion.sizeInUse != 0 ) {
+        // File offset
+        readExecuteRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += readExecuteRegion.bufferSize;
+        regions.push_back({ &readExecuteRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &readExecuteRegion, numRegionVMBytes });
+        numRegionVMBytes += readExecuteRegion.bufferSize;
+    }
+
+    // __BRANCH_STUBS
+    if ( branchStubsRegion.bufferSize != 0 ) {
+        // File offset
+        branchStubsRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += branchStubsRegion.bufferSize;
+        regions.push_back({ &branchStubsRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &branchStubsRegion, numRegionVMBytes });
+        numRegionVMBytes += branchStubsRegion.bufferSize;
+    }
+
+    // __DATA_CONST
+    if ( dataConstRegion.sizeInUse != 0 ) {
+        // File offset
+        dataConstRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += dataConstRegion.bufferSize;
+        regions.push_back({ &dataConstRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &dataConstRegion, numRegionVMBytes });
+        numRegionVMBytes += dataConstRegion.bufferSize;
+    }
+
+    // __BRANCH_GOTS
+    if ( branchGOTsRegion.bufferSize != 0 ) {
+        // File offset
+        branchGOTsRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += branchGOTsRegion.bufferSize;
+        regions.push_back({ &branchGOTsRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &branchGOTsRegion, numRegionVMBytes });
+        numRegionVMBytes += branchGOTsRegion.bufferSize;
+    }
+
+    // -sectcreate
+    // Align to 16k before we lay out all contiguous regions
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    for (CustomSegment& customSegment : customSegments) {
+        Region& region = *customSegment.parentRegion;
+
+        region.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += region.bufferSize;
+        regions.push_back({ &region, 0 });
+        // VM offset
+        // Note we can't align the vm offset in here
+        assert( (numRegionVMBytes % 4096) == 0);
+        regionsVMOrder.push_back({ &region, numRegionVMBytes });
+        numRegionVMBytes += region.bufferSize;
+
+        // Maybe add sections too
+        uint32_t sectionsToAdd = 0;
+        if ( customSegment.sections.size() > 1 ) {
+            // More than one section, so they all need names
+            sectionsToAdd = (uint32_t)customSegment.sections.size();
+        } else if ( !customSegment.sections.front().sectionName.empty() ) {
+            // Only one section, but it has a name
+            sectionsToAdd = 1;
+        }
+        sectionsToAddToRegions[&region] = sectionsToAdd;
+    }
+    numRegionVMBytes = align(numRegionVMBytes, 14);
+
+    // __PRELINK_INFO
+    // Align to 16k
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    if ( prelinkInfoDict != nullptr )
+    {
+        // File offset
+        prelinkInfoRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += prelinkInfoRegion.bufferSize;
+        regions.push_back({ &prelinkInfoRegion, 0 });
+
+        if ( !dataRegionFirstInVMOrder ) {
+            // VM offset
+            numRegionVMBytes = align(numRegionVMBytes, 14);
+            regionsVMOrder.push_back({ &prelinkInfoRegion, numRegionVMBytes });
+            numRegionVMBytes += prelinkInfoRegion.bufferSize;
+        }
+
+        // Add a section too
+        sectionsToAddToRegions[&prelinkInfoRegion] = 1;
+    }
+
+    // Split seg __DATA
+    // Align to 16k
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    if ( readWriteRegion.sizeInUse != 0 ) {
+        // File offset
+        readWriteRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += readWriteRegion.bufferSize;
+        regions.push_back({ &readWriteRegion, 0 });
+
+        if ( !dataRegionFirstInVMOrder ) {
+            // VM offset
+            numRegionVMBytes = align(numRegionVMBytes, 14);
+            regionsVMOrder.push_back({ &readWriteRegion, numRegionVMBytes });
+            numRegionVMBytes += readWriteRegion.bufferSize;
+        }
+    }
+
+    // Split seg __HIB
+    // Align to 16k
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    if ( hibernateRegion.sizeInUse != 0 ) {
+        // File offset
+        hibernateRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += hibernateRegion.bufferSize;
+        regions.push_back({ &hibernateRegion, 0 });
+
+        // VM offset was already handled earlier
+    }
+
+    // Non split seg regions
+    // Align to 16k before we lay out all contiguous regions
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    for (Region& region : nonSplitSegRegions) {
+        region.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += region.bufferSize;
+        regions.push_back({ &region, 0 });
+        // VM offset
+        // Note we can't align the vm offset in here
+        assert( (numRegionVMBytes % 4096) == 0);
+        regionsVMOrder.push_back({ &region, numRegionVMBytes });
+        numRegionVMBytes += region.bufferSize;
+    }
+    numRegionVMBytes = align(numRegionVMBytes, 14);
+
+    // __LINKEDIT
+    // Align to 16k
+    // File offset
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    _readOnlyRegion.cacheFileOffset = numRegionFileBytes;
+    numRegionFileBytes += _readOnlyRegion.bufferSize;
+    regions.push_back({ &_readOnlyRegion, 0 });
+    // VM offset
+    numRegionVMBytes = align(numRegionVMBytes, 14);
+    regionsVMOrder.push_back({ &_readOnlyRegion, numRegionVMBytes });
+    numRegionVMBytes += _readOnlyRegion.bufferSize;
+
+    // __LINKEDIT fixups sub region
+    // Align to 16k
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    if ( fixupsSubRegion.sizeInUse != 0 ) {
+        fixupsSubRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += fixupsSubRegion.bufferSize;
+        //regions.push_back({ &fixupsSubRegion, 0 });
+
+        // VM offset
+        regionsVMOrder.push_back({ &fixupsSubRegion, numRegionVMBytes });
+        numRegionVMBytes += fixupsSubRegion.bufferSize;
+    }
 
     const thread_command* unixThread = nullptr;
     if (const DylibInfo* dylib = getKernelStaticExecutableInputFile()) {
-        unixThread = ((const Header*)dylib->input->mappedFile.mh)->unixThreadLoadCommand();
+        unixThread = dylib->input->mappedFile.mh->unixThreadLoadCommand();
     }
 
     if (_is64) {
@@ -4630,19 +4392,13 @@
         }
 
         // Add an LC_SEGMENT_64 for each region
-        std::unordered_map<const Region*, uint64_t> regionLoadCommandOffsets;
-        for ( const AlignedRegion& region : fileOrder ) {
-            // The fixups sub region doesn't get a load command, as its a range inside LINKEDIT
-            if ( region.region == &fixupsSubRegion )
-                continue;
-
-            regionLoadCommandOffsets[region.region] = cacheHeaderSize + cacheLoadCommandsSize;
-
+        for (auto& regionAndOffset : regions) {
             ++cacheNumLoadCommands;
+            regionAndOffset.second = cacheHeaderSize + cacheLoadCommandsSize;
             cacheLoadCommandsSize += sizeof(segment_command_64);
 
             // Add space for any sections too
-            auto sectionIt = sectionsToAddToRegions.find(region.region);
+            auto sectionIt = sectionsToAddToRegions.find(regionAndOffset.first);
             if ( sectionIt != sectionsToAddToRegions.end() ) {
                 uint32_t numSections = sectionIt->second;
                 cacheLoadCommandsSize += sizeof(section_64) * numSections;
@@ -4664,51 +4420,6 @@
         // Align the app cache header before the rest of the bytes
         cacheHeaderRegionSize = align(cacheHeaderRegionSize, 14);
 
-        // Cache header
-        cacheHeaderRegion.bufferSize            = cacheHeaderRegionSize;
-        cacheHeaderRegion.sizeInUse             = cacheHeaderRegion.bufferSize;
-        cacheHeaderRegion.cacheFileOffset       = 0;
-        cacheHeaderRegion.initProt              = VM_PROT_READ;
-        cacheHeaderRegion.maxProt               = VM_PROT_READ;
-        cacheHeaderRegion.name                  = "__TEXT";
-
-        // Walk all the regions and compute the total file and VM bytes
-        uint64_t numRegionVMBytes = 0;
-        {
-            uint64_t vmAddr = cacheBaseAddress;
-            for ( AlignedRegion& alignedRegion : vmOrder ) {
-                if ( alignedRegion.alignmentBefore != 0 )
-                    vmAddr = align(vmAddr, alignedRegion.alignmentBefore);
-
-                Region* region = alignedRegion.region;
-                region->unslidLoadAddress = vmAddr;
-                vmAddr += region->bufferSize;
-
-                if ( alignedRegion.alignmentAfter != 0 )
-                    vmAddr = align(vmAddr, alignedRegion.alignmentAfter);
-            }
-
-            numRegionVMBytes = vmAddr - cacheBaseAddress;
-        }
-
-        uint64_t numRegionFileBytes = 0;
-        {
-            uint64_t fileOffset = 0;
-            for ( AlignedRegion& alignedRegion : fileOrder ) {
-                if ( alignedRegion.alignmentBefore != 0 )
-                    fileOffset = align(fileOffset, alignedRegion.alignmentBefore);
-
-                Region* region = alignedRegion.region;
-                region->cacheFileOffset = fileOffset;
-                fileOffset += region->bufferSize;
-
-                if ( alignedRegion.alignmentAfter != 0 )
-                    fileOffset = align(fileOffset, alignedRegion.alignmentAfter);
-            }
-
-            numRegionFileBytes = fileOffset;
-        }
-
         assert(numRegionFileBytes <= numRegionVMBytes);
 
         _allocatedBufferSize = cacheHeaderRegionSize + numRegionVMBytes;
@@ -4730,10 +4441,33 @@
 
         // Assign region vm and buffer addresses now that we know the size of
         // the cache header
-        for ( AlignedRegion& alignedRegion : vmOrder ) {
-            Region* region = alignedRegion.region;
-            region->buffer = (uint8_t*)_fullAllocatedBuffer + (region->unslidLoadAddress - cacheBaseAddress);
-        }
+        {
+            // All vm offsets prior to the cache header are already correct
+            // All those after the cache header need to be shifted by the cache
+            // header size
+            bool seenCacheHeader = false;
+            for (const auto& regionAndVMOffset : regionsVMOrder) {
+                Region* region = regionAndVMOffset.first;
+                uint64_t vmOffset = regionAndVMOffset.second;
+                region->unslidLoadAddress = cacheBaseAddress + vmOffset;
+                if ( seenCacheHeader ) {
+                    // Shift by the cache header size
+                    region->unslidLoadAddress += cacheHeaderRegionSize;
+                } else {
+                    // The offset is correct but add in the base address
+                    seenCacheHeader = (region == &cacheHeaderRegion);
+                }
+                region->buffer = (uint8_t*)_fullAllocatedBuffer + (region->unslidLoadAddress - cacheBaseAddress);
+            }
+        }
+
+        // Cache header
+        cacheHeaderRegion.bufferSize            = cacheHeaderRegionSize;
+        cacheHeaderRegion.sizeInUse             = cacheHeaderRegion.bufferSize;
+        cacheHeaderRegion.cacheFileOffset       = 0;
+        cacheHeaderRegion.initProt              = VM_PROT_READ;
+        cacheHeaderRegion.maxProt               = VM_PROT_READ;
+        cacheHeaderRegion.name                  = "__TEXT";
 
 #if 0
         for (const auto& regionAndVMOffset : regionsVMOrder) {
@@ -4765,23 +4499,57 @@
             header.chainedFixups = (linkedit_data_command*)(cacheHeaderRegion.buffer + chainedFixupsOffset);
         }
 
-        for ( AlignedRegion& alignedRegion : fileOrder ) {
-            Region* region = alignedRegion.region;
-            // The fixups sub region doesn't get a load command, as its a range inside LINKEDIT
-            if ( region == &fixupsSubRegion )
-                continue;
-
-            assert(region->initProt != 0);
-            assert(region->maxProt != 0);
-
-            uint64_t loadCommandOffset = regionLoadCommandOffsets.at(region);
-            segment_command_64* loadCommand = (segment_command_64*)(cacheHeaderRegion.buffer + loadCommandOffset);
-            header.segments.push_back({ loadCommand, region });
+        for (auto& regionAndOffset : regions) {
+            assert(regionAndOffset.first->initProt != 0);
+            assert(regionAndOffset.first->maxProt != 0);
+            segment_command_64* loadCommand = (segment_command_64*)(cacheHeaderRegion.buffer + regionAndOffset.second);
+            header.segments.push_back({ loadCommand, regionAndOffset.first });
         }
         for (const auto& dylibAndOffset : dylibs) {
             fileset_entry_command* loadCommand = (fileset_entry_command*)(cacheHeaderRegion.buffer + dylibAndOffset.second);
             header.dylibs.push_back({ loadCommand, dylibAndOffset.first });
         }
+
+        // Move the offsets of all the other regions
+        // Split seg __TEXT
+        readOnlyTextRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // Split seg __TEXT_EXEC
+        readExecuteRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // __BRANCH_STUBS
+        branchStubsRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // Split seg __DATA_CONST
+        dataConstRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // __BRANCH_GOTS
+        branchGOTsRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // Split seg __DATA
+        readWriteRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // Split seg __HIB
+        hibernateRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // -sectcreate
+        for (Region& region : customDataRegions) {
+            region.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+        }
+
+        // Non split seg regions
+        for (Region& region : nonSplitSegRegions) {
+            region.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+        }
+
+        // __PRELINK_INFO
+        prelinkInfoRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // __LINKEDIT
+        _readOnlyRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // __LINKEDIT fixups sub region
+        fixupsSubRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
     } else {
         assert(false);
     }
@@ -4820,7 +4588,7 @@
             macho_build_version_command<P>* cmd = (macho_build_version_command<P>*)header.buildVersion;
             cmd->set_cmd(LC_BUILD_VERSION);
             cmd->set_cmdsize(sizeof(build_version_command));
-            cmd->set_platform(_options.platform.value());
+            cmd->set_platform((uint32_t)_options.platform);
             cmd->set_minos(0);
             cmd->set_sdk(0);
             cmd->set_ntools(0);
@@ -4832,12 +4600,12 @@
         if ( header.unixThread != nullptr ) {
             const DylibInfo* dylib = getKernelStaticExecutableInputFile();
             const dyld3::MachOAnalyzer* ma = dylib->input->mappedFile.mh;
-            ((const Header*)ma)->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
+            ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
                 uint64_t startAddress = dylib->input->mappedFile.mh->entryAddrFromThreadCmd(header.unixThread);
-                if ( (startAddress < info.vmaddr) || (startAddress >= (info.vmaddr + info.vmsize)) )
+                if ( (startAddress < info.vmAddr) || (startAddress >= (info.vmAddr + info.vmSize)) )
                     return;
 
-                uint64_t segSlide = dylib->cacheLocation[info.segmentIndex].dstCacheUnslidAddress - info.vmaddr;
+                uint64_t segSlide = dylib->cacheLocation[info.segIndex].dstCacheUnslidAddress - info.vmAddr;
                 startAddress += segSlide;
 
                 macho_thread_command<P>* cmd = (macho_thread_command<P>*)header.unixThread;
@@ -5100,7 +4868,7 @@
         // Skip codeless kext's
         if ( ma == nullptr )
             continue;
-        uint64_t loadAddress = ((const Header*)ma)->preferredLoadAddress();
+        uint64_t loadAddress = ma->preferredLoadAddress();
 
         // _PrelinkExecutableLoadAddr
         CFNumberRef loadAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &loadAddress);
@@ -5152,10 +4920,10 @@
             }
             __block uint64_t textSegmnentVMAddr = 0;
             __block uint64_t textSegmnentVMSize = 0;
-            ((const Header*)ma)->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
-                if ( info.segmentName == "__TEXT" ) {
-                    textSegmnentVMAddr = info.vmaddr;
-                    textSegmnentVMSize = info.vmsize;
+            ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+                if ( !strcmp(info.segName, "__TEXT") ) {
+                    textSegmnentVMAddr = info.vmAddr;
+                    textSegmnentVMSize = info.vmSize;
                     stop = true;
                 }
             });
@@ -5203,7 +4971,7 @@
         // Make sure we don't have a section with this name already
         if ( section.sectionName.empty() ) {
             // We can't add a segment only section if other sections exist
-            _diagnostics.error("Cannot add empty section name with segment '%s' as other sections exist on that segment",
+            _diagnostics.error("Cannot add empty section name with segment (%s0 as other sections exist on that segment",
                                segmentName.c_str());
             return false;
         }
@@ -5211,13 +4979,13 @@
         for (const CustomSegment::CustomSection& existingSection : segment.sections) {
             if ( existingSection.sectionName.empty() ) {
                 // We can't add a section with a name if an existing section exists with no name
-                _diagnostics.error("Cannot add section named '%s' with segment '%s' as segment has existing nameless section",
+                _diagnostics.error("Cannot add section named (%s) with segment (%s) as segment has existing nameless section",
                                    segmentName.c_str(), section.sectionName.c_str());
                 return false;
             }
             if ( existingSection.sectionName == section.sectionName ) {
                 // We can't add a section with the same name as an existing one
-                _diagnostics.error("Cannot add section named '%s' with segment '%s' as section already exists",
+                _diagnostics.error("Cannot add section named (%s) with segment (%s) as section already exists",
                                    segmentName.c_str(), section.sectionName.c_str());
                 return false;
             }
@@ -5277,9 +5045,6 @@
         }
         return;
     }
-
-    // Find stubs to remove, if any
-    parseStubs();
 
     // assign addresses for each segment of each dylib in new cache
     assignSegmentRegionsAndOffsets();
@@ -5331,20 +5096,6 @@
                 lastDataRegion = &dataConstRegion;
         }
 
-        if ( lateConstRegion.sizeInUse != 0 ) {
-            if ( firstDataRegion == nullptr )
-                firstDataRegion = &lateConstRegion;
-            if ( (lastDataRegion == nullptr) || (lateConstRegion.buffer > lastDataRegion->buffer) )
-                lastDataRegion = &lateConstRegion;
-        }
-
-        if ( dataSptmRegion.sizeInUse != 0 ) {
-            if ( firstDataRegion == nullptr )
-                firstDataRegion = &dataSptmRegion;
-            if ( (lastDataRegion == nullptr) || (dataSptmRegion.buffer > lastDataRegion->buffer) )
-                lastDataRegion = &dataSptmRegion;
-        }
-
         if ( branchGOTsRegion.bufferSize != 0 ) {
             if ( firstDataRegion == nullptr )
                 firstDataRegion = &branchGOTsRegion;
@@ -5378,7 +5129,7 @@
             _aslrTracker.setDataRegion(firstDataRegion->buffer, size);
         }
     }
-    adjustAllImagesForNewSegmentLocations(cacheBaseAddress, nullptr);
+    adjustAllImagesForNewSegmentLocations(cacheBaseAddress, nullptr, nullptr);
     if ( _diagnostics.hasError() )
         return;
 
@@ -5397,17 +5148,12 @@
 
     // optimize away stubs
     uint64_t t6 = mach_absolute_time();
-
-    if ( removeStubs() ) {
-        // Stubs were removed, but we need to rewrite calls which would have gone through those stubs
-        rewriteRemovedStubs();
-    } else {
-        // Stubs weren't removed, so do the existing stub optimizer
-        __block std::vector<StubOptimizerInfo> images;
+    {
+        __block std::vector<std::pair<const mach_header*, const char*>> images;
         forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
                             DylibStripMode stripMode, const std::vector<std::string>& dependencies,
                             Diagnostics& dylibDiag, bool& stop) {
-            images.push_back({ ma, dylibID.c_str(), nullptr, nullptr });
+            images.push_back({ ma, dylibID.c_str() });
         });
         // FIXME: Should we keep the same never stub eliminate symbols?  Eg, for gmalloc.
         const char* const neverStubEliminateSymbols[] = {
@@ -5416,8 +5162,8 @@
 
         uint64_t cacheUnslidAddr = cacheBaseAddress;
         int64_t cacheSlide = (long)_fullAllocatedBuffer - cacheUnslidAddr;
-        std::unordered_map<uint64_t, std::pair<uint64_t, uint8_t*>> stubsToIslandAddr;
-        optimizeAwayStubs(images, cacheSlide, nullptr, stubsToIslandAddr, neverStubEliminateSymbols);
+        optimizeAwayStubs(images, cacheSlide, cacheUnslidAddr,
+                          nullptr, neverStubEliminateSymbols);
     }
 
     // FIPS seal corecrypto, This must be done after stub elimination (so that __TEXT,__text is not changed after sealing)
@@ -5537,213 +5283,6 @@
     }
 }
 
-static void getSectionLayout(const dyld3::MachOAnalyzer* ma,
-                             std::vector<uint64_t>& sectionAddresses,
-                             std::vector<uint8_t*>& sectionBuffers,
-                             uint32_t& authStubSectionIndex)
-{
-    // section index 0 refers to mach_header
-    sectionAddresses.push_back(((const Header*)ma)->preferredLoadAddress());
-    sectionBuffers.push_back(nullptr);
-
-    intptr_t slide = ma->getSlide();
-    ((const Header*)ma)->forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
-        if ( (sectInfo.segmentName == "__TEXT_EXEC") && (sectInfo.sectionName == "__auth_stubs") )
-            authStubSectionIndex = (uint32_t)sectionAddresses.size();
-        sectionAddresses.push_back(sectInfo.address);
-        sectionBuffers.push_back((uint8_t*)sectInfo.address + slide);
-    });
-}
-
-// Auth stubs will load a value then jump to it.  This returns the list of locations they jump to
-static void getAuthStubTargets(Diagnostics& diag, const dyld3::MachOAnalyzer* ma,
-                               std::string_view dylibID,
-                               const std::vector<uint64_t>& sectionAddresses,
-                               const std::vector<uint8_t*>& sectionBuffers,
-                               uint32_t stubSectionIndex,
-                               std::map<uint64_t, uint64_t>& stubToTargetMap)
-{
-    uint32_t splitSegSize = 0;
-    const uint8_t* infoStart = (const uint8_t*)ma->getSplitSeg(splitSegSize);
-    const uint8_t* infoEnd = infoStart + splitSegSize;
-    if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) {
-        diag.error("malformed split seg info in %s", dylibID.data());
-        return;
-    }
-
-    // Whole         :== <count> FromToSection+
-    // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
-    // ToOffset      :== <to-sect-offset-delta> <count> FromOffset+
-    // FromOffset    :== <kind> <count> <from-sect-offset-delta>
-    const uint8_t* p = infoStart;
-    uint64_t sectionCount = read_uleb128(p, infoEnd);
-    for (uint64_t i=0; i < sectionCount; ++i) {
-        uint64_t fromSectionIndex = read_uleb128(p, infoEnd);
-        uint64_t toSectionIndex = read_uleb128(p, infoEnd);
-        uint64_t toOffsetCount = read_uleb128(p, infoEnd);
-        uint64_t toSectionOffset = 0;
-        for (uint64_t j=0; j < toOffsetCount; ++j) {
-            uint64_t toSectionDelta = read_uleb128(p, infoEnd);
-            uint64_t fromOffsetCount = read_uleb128(p, infoEnd);
-            toSectionOffset += toSectionDelta;
-            for (uint64_t k=0; k < fromOffsetCount; ++k) {
-                uint64_t kind = read_uleb128(p, infoEnd);
-                if ( kind > 13 ) {
-                    diag.error("bad kind (%llu) value in %s\n", kind, dylibID.data());
-                }
-                uint64_t fromSectDeltaCount = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
-                if ( diag.hasError() )
-                    return;
-                uint64_t fromSectionOffset = 0;
-                for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
-                    uint64_t delta = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
-                    if ( diag.hasError() )
-                        return;
-                    fromSectionOffset += delta;
-                    if ( fromSectionIndex == stubSectionIndex ) {
-                        // The stub is 16-bytes in size, and contains the stub fixup here.
-                        // We need to work out the stub for the fixup
-                        uint64_t stubOffset = fromSectionOffset & ~0xF;
-                        uint64_t stubAddr = sectionAddresses[fromSectionIndex] + stubOffset;
-
-                        uint64_t* gotPtr = (uint64_t*)(sectionBuffers[toSectionIndex] + toSectionOffset);
-                        uint64_t gotTargetAddr = *gotPtr;
-                        printf("");
-
-                        stubToTargetMap[stubAddr] = gotTargetAddr;
-                    }
-                }
-            }
-        }
-    }
-}
-
-static void rewriteBranchesToStubs(Diagnostics& diag, const dyld3::MachOAnalyzer* ma,
-                                   std::string_view dylibID,
-                                   const std::vector<uint64_t>& sectionAddresses,
-                                   const std::vector<uint8_t*>& sectionBuffers,
-                                   uint32_t stubSectionIndex,
-                                   const std::map<uint64_t, uint64_t>& stubToTargetMap)
-{
-    static const int64_t b128MegLimit = 0x07FFFFFF;
-
-    uint32_t splitSegSize = 0;
-    const uint8_t* infoStart = (const uint8_t*)ma->getSplitSeg(splitSegSize);
-    const uint8_t* infoEnd = infoStart + splitSegSize;
-    if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) {
-        diag.error("malformed split seg info in %s", dylibID.data());
-        return;
-    }
-
-    // Whole         :== <count> FromToSection+
-    // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
-    // ToOffset      :== <to-sect-offset-delta> <count> FromOffset+
-    // FromOffset    :== <kind> <count> <from-sect-offset-delta>
-    const uint8_t* p = infoStart;
-    uint64_t sectionCount = read_uleb128(p, infoEnd);
-    for (uint64_t i=0; i < sectionCount; ++i) {
-        uint64_t fromSectionIndex = read_uleb128(p, infoEnd);
-        uint64_t toSectionIndex = read_uleb128(p, infoEnd);
-        uint64_t toOffsetCount = read_uleb128(p, infoEnd);
-        uint64_t toSectionOffset = 0;
-        for (uint64_t j=0; j < toOffsetCount; ++j) {
-            uint64_t toSectionDelta = read_uleb128(p, infoEnd);
-            uint64_t fromOffsetCount = read_uleb128(p, infoEnd);
-            toSectionOffset += toSectionDelta;
-            for (uint64_t k=0; k < fromOffsetCount; ++k) {
-                uint64_t kind = read_uleb128(p, infoEnd);
-                if ( kind > 13 ) {
-                    diag.error("bad kind (%llu) value in %s\n", kind, dylibID.data());
-                }
-                uint64_t fromSectDeltaCount = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
-                if ( diag.hasError() )
-                    return;
-                uint64_t fromSectionOffset = 0;
-                for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
-                    uint64_t delta = dyld3::MachOFile::read_uleb128(diag, p, infoEnd);
-                    if ( diag.hasError() )
-                        return;
-                    fromSectionOffset += delta;
-                    if ( toSectionIndex == stubSectionIndex ) {
-                        // The stub is 16-bytes in size, and contains the stub fixup here.
-                        // We need to work out the stub for the fixup
-                        uint32_t* instrPtr = (uint32_t*)(sectionBuffers[fromSectionIndex] + fromSectionOffset);
-                        uint64_t instrAddr = sectionAddresses[fromSectionIndex] + fromSectionOffset;
-                        uint64_t stubAddr = sectionAddresses[toSectionIndex] + toSectionOffset;
-
-                        auto it = stubToTargetMap.find(stubAddr);
-                        if ( it == stubToTargetMap.end() ) {
-                            diag.error("couldn't find stub at 0x%llx, for branch 0x%llx in %s\n", stubAddr, instrAddr, dylibID.data());
-                        }
-
-                        uint64_t finalTargetAddr = it->second;
-
-                        if ( kind != DYLD_CACHE_ADJ_V2_ARM64_BR26 ) {
-                            diag.error("bad kind (%llu) value in %s\n", kind, dylibID.data());
-                            return;
-                        }
-                        // skip all but BL or B
-                        uint32_t& instruction = *instrPtr;
-                        if ( (instruction & 0x7C000000) != 0x14000000 ) {
-                            diag.error("bad instruction (0x%x) value in %s\n", instruction, dylibID.data());
-                            return;
-                        }
-
-                        int64_t deltaToFinalTarget = finalTargetAddr - instrAddr;
-                        // if final target within range, change to branch there directly
-                        if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) {
-                            instruction = (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF);
-                        } else {
-                            diag.error("branch (%llx -> %llx) out of reach (%llx) in %s\n", fromSectionOffset, toSectionOffset,
-                                       deltaToFinalTarget, dylibID.data());
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-void AppCacheBuilder::rewriteRemovedStubs()
-{
-    if ( _diagnostics.hasError() )
-        return;
-
-    for (AppCacheDylibInfo& dylib : sortedDylibs) {
-        if ( !dylib._coalescer.auth_stubs.sectionIsObliterated )
-            continue;
-
-        const dyld3::MachOAnalyzer* ma = nullptr;
-        for (const SegmentMappingInfo& loc : dylib.cacheLocation) {
-            if ( loc.segName == "__TEXT" ) {
-                // Assume __TEXT contains the mach header
-                ma = (const dyld3::MachOAnalyzer*)loc.dstSegment;
-                break;
-            }
-        }
-
-        // We need to find what the auth stubs pointed to, then rewrite all
-        // users of the auth stubs to jump to those locations instead
-        std::vector<uint64_t> sectionAddresses;
-        std::vector<uint8_t*> sectionBuffers;
-        uint32_t authStubSectionIndex = ~0U;
-        getSectionLayout(ma, sectionAddresses, sectionBuffers, authStubSectionIndex);
-
-        std::map<uint64_t, uint64_t> stubToTargetMap;
-        getAuthStubTargets(_diagnostics, dylib.input->mappedFile.mh, dylib.dylibID,
-                           sectionAddresses, sectionBuffers, authStubSectionIndex,
-                           stubToTargetMap);
-        if ( _diagnostics.hasError() )
-            return;
-
-        rewriteBranchesToStubs(_diagnostics, dylib.input->mappedFile.mh, dylib.dylibID,
-                               sectionAddresses, sectionBuffers, authStubSectionIndex,
-                               stubToTargetMap);
-        if ( _diagnostics.hasError() )
-            return;
-    }
-}
-
 void AppCacheBuilder::fipsSign()
 {
     if ( appCacheOptions.cacheKind != Options::AppCacheKind::kernel )
@@ -5860,7 +5399,7 @@
     strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
     int fd = mkstemp(pathTemplateSpace);
     if ( fd == -1 ) {
-        _diagnostics.error("could not open file %s", pathTemplateSpace);
+        _diagnostics.error("could not open file '%s'", pathTemplateSpace);
         return;
     }
     uint64_t cacheFileSize = 0;