Loading...
common/MachOAnalyzer.cpp dyld-1340 dyld-1162
--- dyld/dyld-1340/common/MachOAnalyzer.cpp
+++ dyld/dyld-1162/common/MachOAnalyzer.cpp
@@ -46,21 +46,15 @@
 #include "MachOAnalyzer.h"
 #include "CodeSigningTypes.h"
 #include "Array.h"
-#include "Header.h"
-#include "Universal.h"
 
 // FIXME: We should get this from cctools
 #define DYLD_CACHE_ADJ_V2_FORMAT 0x7F
 
-using mach_o::Header;
-using mach_o::Platform;
-using mach_o::GradedArchitectures;
-using mach_o::Universal;
-
 namespace dyld3 {
 
+
 bool MachOAnalyzer::isValidMainExecutable(Diagnostics& diag, const char* path, uint64_t sliceLength,
-                                          const GradedArchitectures& archs, Platform platform) const
+                                          const GradedArchs& archs, Platform platform) const
 {
     if ( !this->validMachOForArchAndPlatform(diag, (size_t)sliceLength, path, archs, platform, true) )
         return false;
@@ -78,28 +72,27 @@
 
 #if !TARGET_OS_EXCLAVEKIT
 bool MachOAnalyzer::loadFromBuffer(Diagnostics& diag, const closure::FileSystem& fileSystem,
-                                   const char* path, const GradedArchitectures& archs, Platform platform,
+                                   const char* path, const GradedArchs& archs, Platform platform,
                                    closure::LoadedFileInfo& info)
 {
     // if fat, remap just slice needed
-    bool fatButMissingSlice = false;
-    std::span<const uint8_t> content = {(const uint8_t*)info.fileContent, (size_t)info.fileContentLen};
-    if ( const Universal* uni = Universal::isUniversal(content) ) {
-        Universal::Slice slice;
-        if ( uni->bestSlice(archs, info.isOSBinary, slice) ) {
-            uint64_t sliceOffset = slice.buffer.data() - content.data();
-            // unmap anything before slice
-            fileSystem.unloadPartialFile(info, sliceOffset, slice.buffer.size());
-            // Update the info to keep track of the new slice offset.
-            info.sliceOffset = sliceOffset;
-            info.sliceLen = slice.buffer.size();
-        }
-        else {
-            fatButMissingSlice = true;
-        }
-    }
-    
-    if ( fatButMissingSlice ) {
+    bool fatButMissingSlice;
+    const FatFile*       fh = (FatFile*)info.fileContent;
+    uint64_t sliceOffset = info.sliceOffset;
+    uint64_t sliceLen = info.sliceLen;
+    if ( fh->isFatFileWithSlice(diag, info.fileContentLen, archs, info.isOSBinary, sliceOffset, sliceLen, fatButMissingSlice) ) {
+        // unmap anything before slice
+        fileSystem.unloadPartialFile(info, sliceOffset, sliceLen);
+        // Update the info to keep track of the new slice offset.
+        info.sliceOffset = sliceOffset;
+        info.sliceLen = sliceLen;
+    }
+    else if ( diag.hasError() ) {
+        // We must have generated an error in the fat file parsing so use that error
+        fileSystem.unloadFile(info);
+        return false;
+    }
+    else if ( fatButMissingSlice ) {
         diag.error("missing compatible arch in %s", path);
         fileSystem.unloadFile(info);
         return false;
@@ -138,7 +131,7 @@
 
 
 closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::FileSystem& fileSystem,
-                                            const char* path, const GradedArchitectures& archs, Platform platform, char realerPath[PATH_MAX])
+                                            const char* path, const GradedArchs& archs, Platform platform, char realerPath[PATH_MAX])
 {
     // FIXME: This should probably be an assert, but if we happen to have a diagnostic here then something is wrong
     // above us and we should quickly return instead of doing unnecessary work.
@@ -150,7 +143,7 @@
         = ^(const char *format, ...) __printflike(1, 2) {
             va_list list;
             va_start(list, format);
-            diag.error(format, va_list_wrap(list));
+            diag.error(format, list);
             va_end(list);
         };
     if ( !fileSystem.loadFile(path, info, realerPath, fileErrorLog) ) {
@@ -178,7 +171,7 @@
 
     uint32_t sigOffset;
     uint32_t sigSize;
-    if ( !((const Header*)this)->hasCodeSignature(sigOffset, sigSize) )
+    if ( !this->hasCodeSignature(sigOffset, sigSize) )
         return false;
 
     // register code signature
@@ -230,7 +223,7 @@
 }
 #endif
 
-bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const GradedArchitectures& archs, Platform reqPlatform, bool isOSBinary, bool internalInstall) const
+bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const GradedArchs& archs, Platform reqPlatform, bool isOSBinary, bool internalInstall) const
 {
     // must start with mach-o magic value
     if ( (this->magic != MH_MAGIC) && (this->magic != MH_MAGIC_64) ) {
@@ -238,7 +231,7 @@
         return false;
     }
 
-    if ( !archs.isCompatible(((const Header*)this)->arch(), isOSBinary) ) {
+    if ( !archs.grade(this->cputype, this->cpusubtype, isOSBinary) ) {
         diag.error("could not use '%s' because it is not a compatible arch", path);
         return false;
     }
@@ -250,7 +243,7 @@
         case MH_BUNDLE:
         case MH_DYLINKER:
            break;
-#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
+#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
         // Allow offline tools to analyze binaries dyld doesn't load
         case MH_KEXT_BUNDLE:
         case MH_FILESET:
@@ -280,18 +273,39 @@
 #if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
     if ( isFileSet() ) {
         // A statically linked kernel collection should contain a 0 platform
-        mach_o::PlatformAndVersions pvs = ((const Header*)this)->platformAndVersions();
-        if ( !pvs.platform.empty() ) {
+        __block bool foundPlatform = false;
+        __block bool foundBadPlatform = false;
+        forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+            foundPlatform = true;
+            if ( platform != Platform::unknown ) {
+                foundBadPlatform = true;
+            }
+        });
+        if (!foundPlatform) {
+            diag.error("could not use '%s' because we expected it to have a platform", path);
+            return false;
+        }
+        if (foundBadPlatform) {
             diag.error("could not use '%s' because is has the wrong platform", path);
             return false;
         }
-    } else if ( reqPlatform.empty() ) {
-        // This is handled elsewhere in the kernel collection builder, where we have access
-        // to the kernel binary and can infer its platform
+    } else if ( reqPlatform == Platform::unknown ) {
+        // Unfortunately the static kernel has a platform, but kext's don't, so we can't
+        // verify the platform of the kernel.
+        if ( !isStaticExecutable() ) {
+            __block bool foundPlatform = false;
+            forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+                foundPlatform = true;
+            });
+            if (foundPlatform) {
+                diag.error("could not use '%s' because we expected it to have no platform", path);
+                return false;
+            }
+        }
     } else
 #endif
-    if ( !((const Header*)this)->loadableIntoProcess(reqPlatform, path, internalInstall) ) {
-        diag.error("could not use '%s' because it was not built for platform %s", path, reqPlatform.name().c_str());
+    if ( !this->loadableIntoProcess(reqPlatform, path, internalInstall) ) {
+        diag.error("could not use '%s' because it was not built for platform %s", path, MachOFile::platformName(reqPlatform));
         return false;
     }
 
@@ -373,8 +387,8 @@
 
     // check load commands fit in TEXT segment
     __block bool foundTEXT    = false;
-    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-        if ( info.segmentName == "__TEXT" ) {
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
             foundTEXT = true;
             if ( this->sizeofcmds + machHeaderSize() > info.fileSize ) {
                 diag.error("in '%s' load commands exceed length of __TEXT segment", path);
@@ -411,17 +425,17 @@
 #if BUILDING_APP_CACHE_UTIL
         // 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*)this)->forEachSegment(^(const Header::SegmentInfo& segmentInfo, bool& stop) {
-            baseAddress = std::min(baseAddress, segmentInfo.vmaddr);
+        forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
+            baseAddress = std::min(baseAddress, segmentInfo.vmAddr);
         });
-        uint64_t textSegVMAddr = ((const Header*)this)->preferredLoadAddress();
+        uint64_t textSegVMAddr = preferredLoadAddress();
 #else
-        uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
+        uint64_t baseAddress = preferredLoadAddress();
 #endif
 
-        ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& segmentInfo, bool& stop) {
-            if ( (segmentInfo.fileSize != 0) && (segmentInfo.vmsize != 0) ) {
-                kern_return_t r = vm_copy(mach_task_self(), (vm_address_t)((long)info.fileContent+segmentInfo.fileOffset), (vm_size_t)segmentInfo.fileSize, (vm_address_t)(newMappedAddr+segmentInfo.vmaddr-baseAddress));
+        forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
+            if ( (segmentInfo.fileSize != 0) && (segmentInfo.vmSize != 0) ) {
+                kern_return_t r = vm_copy(mach_task_self(), (vm_address_t)((long)info.fileContent+segmentInfo.fileOffset), (vm_size_t)segmentInfo.fileSize, (vm_address_t)(newMappedAddr+segmentInfo.vmAddr-baseAddress));
                 if ( r != KERN_SUCCESS ) {
                     diag.error("vm_copy() failure");
                     stop = true;
@@ -440,12 +454,12 @@
                 info.unload = [](const closure::LoadedFileInfo& info) {
                     // Unloading binaries where __DATA is first requires working out the real range of the binary
                     // The fileContent points at the mach_header, not the actaul start of the file content, unfortunately.
-                    const Header* hdr = (const Header*)info.fileContent;
+                    const MachOAnalyzer* ma = (const MachOAnalyzer*)info.fileContent;
                     __block uint64_t baseAddress = ~0ULL;
-                    hdr->forEachSegment(^(const Header::SegmentInfo& segInfo, bool& stop) {
-                        baseAddress = std::min(baseAddress, segInfo.vmaddr);
+                    ma->forEachSegment(^(const SegmentInfo& segInfo, bool& stop) {
+                        baseAddress = std::min(baseAddress, segInfo.vmAddr);
                     });
-                    uint64_t textSegVMAddr = hdr->preferredLoadAddress();
+                    uint64_t textSegVMAddr = ma->preferredLoadAddress();
 
                     uint64_t basePointerOffset = textSegVMAddr - baseAddress;
                     uint8_t* bufferStart = (uint8_t*)info.fileContent - basePointerOffset;
@@ -583,23 +597,10 @@
 
     // all new binaries must link with something else
     if ( (dependentsCount == 0) && enforceFormat(Malformed::noLinkedDylibs) ) {
-        const Header *hdr = (const Header*)this;
-        const char* libSystemDir = hdr->builtForPlatform(Platform::driverKit, true) ? "/System/DriverKit/usr/lib/system/" : "/usr/lib/system/";
+        const char* libSystemDir = this->builtForPlatform(Platform::driverKit, true) ? "/System/DriverKit/usr/lib/system/" : "/usr/lib/system/";
         // except for dylibs in libSystem.dylib which are ok to link with nothing (they are on bottom)
         bool isNotLibSystem = (installName == nullptr) || (strncmp(installName, libSystemDir, strlen(libSystemDir)) != 0);
 
-        if ( internalInstall && (   hdr->builtForPlatform(Platform::macOS_exclaveKit, true)
-                                 || hdr->builtForPlatform(Platform::iOS_exclaveKit, true)
-                                 || hdr->builtForPlatform(Platform::tvOS_exclaveKit, true)
-                                 || hdr->builtForPlatform(Platform::watchOS_exclaveKit, true)
-                                 || hdr->builtForPlatform(Platform::visionOS_exclaveKit, true)) ) {
-            // The path of ExclaveKit libSystem libraries starts with /System/ExclaveKit
-            const size_t prefixLength = 18;
-            isNotLibSystem = true;
-            if ( installName != nullptr && strlen(installName) > prefixLength )
-                if ( strncmp(installName + prefixLength, "/usr/lib/system/", 16) == 0 )
-                    isNotLibSystem = false;
-        }
         if ( this->isDyldManaged() && isNotLibSystem ) {
             diag.error("in '%s' missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)", path);
             return false;
@@ -624,15 +625,15 @@
             case LC_MAIN: {
                 ++mainCount;
                 entry_point_command* mainCmd = (entry_point_command*)cmd;
-                uint64_t startAddress = ((const Header*)this)->preferredLoadAddress() + mainCmd->entryoff;
+                uint64_t startAddress = preferredLoadAddress() + mainCmd->entryoff;
 
                 __block bool foundSegment = false;
-                ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stopSegment) {
+                forEachSegment(^(const SegmentInfo& info, bool& stopSegment) {
                     // Skip segments which don't contain this address
-                    if ( (startAddress < info.vmaddr) || (startAddress >= info.vmaddr+info.vmsize) )
+                    if ( (startAddress < info.vmAddr) || (startAddress >= info.vmAddr+info.vmSize) )
                         return;
                     foundSegment = true;
-                    if ( !info.executable() )
+                    if ( (info.protections & VM_PROT_EXECUTE) == 0 )
                         diag.error("LC_MAIN points to non-executable segment");
                     stopSegment = true;
                 });
@@ -650,12 +651,12 @@
                     stop = true;
                 } else {
                     __block bool foundSegment = false;
-                    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stopSegment) {
+                    forEachSegment(^(const SegmentInfo& info, bool& stopSegment) {
                         // Skip segments which don't contain this address
-                        if ( (startAddress < info.vmaddr) || (startAddress >= info.vmaddr+info.vmsize) )
+                        if ( (startAddress < info.vmAddr) || (startAddress >= info.vmAddr+info.vmSize) )
                             return;
                         foundSegment = true;
-                        if ( !info.executable() ) {
+                        if ( (info.protections & VM_PROT_EXECUTE) == 0 ) {
                             // Suppress this error for the x86_64 kernel
                             if ( !this->isStaticExecutable() )
                                 diag.error("LC_UNIXTHREAD points to non-executable segment");
@@ -673,7 +674,7 @@
     if ( diag.hasError() )
         return false;
 
-    if ( ((const Header*)this)->builtForPlatform(Platform::driverKit) ) {
+    if ( this->builtForPlatform(Platform::driverKit) ) {
         if ( mainCount + threadCount == 0 )
             return true;
         diag.error("LC_MAIN not allowed for driverkit");
@@ -682,6 +683,7 @@
 
     if ( mainCount+threadCount == 1 )
         return true;
+
     if ( mainCount + threadCount == 0 )
         diag.error("missing LC_MAIN or LC_UNIXTHREAD");
     else
@@ -731,7 +733,7 @@
 
 
 
-bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                                       bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind) const
 {
     if ( !segIndexSet ) {
@@ -742,8 +744,8 @@
         diag.error("in '%s' %s segment index %d too large", path, opcodeName, segmentIndex);
         return true;
     }
-    if ( segmentOffset > (segments[segmentIndex].vmsize - ptrSize) ) {
-        diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmsize);
+    if ( segmentOffset > (segments[segmentIndex].vmSize-ptrSize) ) {
+        diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmSize);
         return true;
     }
     switch ( kind )  {
@@ -760,6 +762,10 @@
             break;
         case Rebase::textAbsolute32:
         case Rebase::textPCrel32:
+            if ( !segments[segmentIndex].textRelocs ) {
+                diag.error("in '%s' %s text rebase is in segment that does not support text relocations", path, opcodeName);
+                return true;
+            }
             if ( segments[segmentIndex].writable() ) {
                 diag.error("in '%s' %s text rebase is in writable segment", path, opcodeName);
                 return true;
@@ -777,17 +783,17 @@
 }
 
 
-void MachOAnalyzer::getAllSegmentsInfos(Diagnostics& diag, Header::SegmentInfo segments[]) const
-{
-    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-        segments[info.segmentIndex] = info;
+void MachOAnalyzer::getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const
+{
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        segments[info.segIndex] = info;
     });
 }
 
 
 bool MachOAnalyzer::validRebaseInfo(Diagnostics& diag, const char* path) const
 {
-    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                           bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
         if ( invalidRebaseState(diag, opcodeName, path, leInfo, segments, segIndexSet, ptrSize, segmentIndex, segmentOffset, kind) )
             stop = true;
@@ -800,20 +806,20 @@
 {
     __block bool     startVmAddrSet = false;
     __block uint64_t startVmAddr    = 0;
-    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                           bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
         if ( kind != Rebase::textAbsolute32 )
             return;
         if ( !startVmAddrSet ) {
             for (int i=0; i <= segmentIndex; ++i) {
-                if ( segments[i].segmentName == "__TEXT" ) {
-                    startVmAddr = segments[i].vmaddr;
+                if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+                    startVmAddr = segments[i].vmAddr;
                     startVmAddrSet = true;
                     break;
                }
             }
         }
-        uint64_t rebaseVmAddr  = segments[segmentIndex].vmaddr + segmentOffset;
+        uint64_t rebaseVmAddr  = segments[segmentIndex].vmAddr + segmentOffset;
         uint64_t runtimeOffset = rebaseVmAddr - startVmAddr;
         handler(runtimeOffset, stop);
     });
@@ -827,17 +833,17 @@
     __block uint64_t lpEndVmAddr    = 0;
     __block uint64_t shVmAddr       = 0;
     __block uint64_t shEndVmAddr    = 0;
-    forEachSection(^(const Header::SectionInfo& info, bool &stop) {
-        if ( (info.flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
-            lpVmAddr    = info.address;
-            lpEndVmAddr = info.address + info.size;
-        }
-        else if ( (info.flags & S_ATTR_PURE_INSTRUCTIONS) && (info.sectionName == "__stub_helper") ) {
-            shVmAddr    = info.address;
-            shEndVmAddr = info.address + info.size;
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( (info.sectFlags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
+            lpVmAddr    = info.sectAddr;
+            lpEndVmAddr = info.sectAddr + info.sectSize;
+        }
+        else if ( (info.sectFlags & S_ATTR_PURE_INSTRUCTIONS) && (strcmp(info.sectName, "__stub_helper") == 0) ) {
+            shVmAddr    = info.sectAddr;
+            shEndVmAddr = info.sectAddr + info.sectSize;
         }
     });
-    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                           bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
         switch ( kind ) {
             case Rebase::unknown:
@@ -852,14 +858,14 @@
         }
         if ( !startVmAddrSet ) {
             for (int i=0; i < segmentIndex; ++i) {
-                if ( segments[i].segmentName == "__TEXT" ) {
-                    startVmAddr = segments[i].vmaddr;
+                if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+                    startVmAddr = segments[i].vmAddr;
                     startVmAddrSet = true;
                     break;
                }
             }
         }
-        uint64_t rebaseVmAddr  = segments[segmentIndex].vmaddr + segmentOffset;
+        uint64_t rebaseVmAddr  = segments[segmentIndex].vmAddr + segmentOffset;
         bool isLazyPointerRebase = false;
         if ( (rebaseVmAddr >= lpVmAddr) && (rebaseVmAddr < lpEndVmAddr) ) {
             // rebase is in lazy pointer section
@@ -915,7 +921,7 @@
 
 
 void MachOAnalyzer::forEachRebase(Diagnostics& diag,
-                                 void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+                                 void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                                                  bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
                                                  Rebase kind, bool& stop)) const
 {
@@ -924,7 +930,7 @@
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
@@ -1110,51 +1116,51 @@
 #endif // SUPPORT_CLASSIC_RELOCS
 }
 
-bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr, const Header::SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const
+bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const
 {
     for (uint32_t i=0; i < segCount; ++i) {
-        if ( (segmentsInfos[i].vmaddr <= addr) && (addr < segmentsInfos[i].vmaddr+segmentsInfos[i].vmsize) ) {
+        if ( (segmentsInfos[i].vmAddr <= addr) && (addr < segmentsInfos[i].vmAddr+segmentsInfos[i].vmSize) ) {
             segIndex  = i;
-            segOffset = addr - segmentsInfos[i].vmaddr;
+            segOffset = addr - segmentsInfos[i].vmAddr;
             return true;
         }
     }
     return false;
 }
 
-uint64_t MachOAnalyzer::localRelocBaseAddress(const Header::SegmentInfo segmentsInfos[], uint32_t segCount) const
+uint64_t MachOAnalyzer::localRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
 {
     if ( isArch("x86_64") || isArch("x86_64h") ) {
 #if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
         if ( isKextBundle() ) {
             // for kext bundles the reloc base address starts at __TEXT segment
-            return segmentsInfos[0].vmaddr;
+            return segmentsInfos[0].vmAddr;
         }
 #endif
         // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA)
         for (uint32_t i=0; i < segCount; ++i) {
             if ( segmentsInfos[i].writable() )
-                return segmentsInfos[i].vmaddr;
+                return segmentsInfos[i].vmAddr;
         }
     }
     // reloc base address is start of TEXT segment
-    if ( this->isMainExecutable() && (segmentsInfos[0].initProt == 0) )
-        return segmentsInfos[1].vmaddr;
+    if ( this->isMainExecutable() && (segmentsInfos[0].protections == 0) )
+        return segmentsInfos[1].vmAddr;
     else
-        return segmentsInfos[0].vmaddr;
-}
-
-uint64_t MachOAnalyzer::externalRelocBaseAddress(const Header::SegmentInfo segmentsInfos[], uint32_t segCount) const
+        return segmentsInfos[0].vmAddr;
+}
+
+uint64_t MachOAnalyzer::externalRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
 {
     // Dyld caches are too large for a raw r_address, so everything is an offset from the base address
     if ( inDyldCache() ) {
-        return ((const Header*)this)->preferredLoadAddress();
+        return preferredLoadAddress();
     }
 
 #if BUILDING_APP_CACHE_UTIL || BUILDING_DYLDINFO
     if ( isKextBundle() ) {
         // for kext bundles the reloc base address starts at __TEXT segment
-        return ((const Header*)this)->preferredLoadAddress();
+        return preferredLoadAddress();
     }
 #endif
 
@@ -1162,7 +1168,7 @@
         // for x86_64 reloc base address starts at first writable segment (usually __DATA)
         for (uint32_t i=0; i < segCount; ++i) {
             if ( segmentsInfos[i].writable() )
-                return segmentsInfos[i].vmaddr;
+                return segmentsInfos[i].vmAddr;
         }
     }
     // For everyone else we start at 0
@@ -1198,9 +1204,9 @@
     if ( (indirectSymbolTableCount == 0) && isKextBundle() )
         return;
 
-    forEachSection(^(const Header::SectionInfo& sectInfo, bool& sectionStop) {
-        uint8_t  sectionType  = (sectInfo.flags & SECTION_TYPE);
-        bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (sectInfo.flags & S_ATTR_SELF_MODIFYING_CODE) && (sectInfo.reserved2 == 5) && (this->cputype == CPU_TYPE_I386);
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& sectionStop) {
+        uint8_t  sectionType  = (sectInfo.sectFlags & SECTION_TYPE);
+        bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (sectInfo.sectFlags & S_ATTR_SELF_MODIFYING_CODE) && (sectInfo.reserved2 == 5) && (this->cputype == CPU_TYPE_I386);
         if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && !selfModifyingStub )
             return;
         if ( (flags & S_ATTR_SELF_MODIFYING_CODE) && !selfModifyingStub ) {
@@ -1209,9 +1215,9 @@
             return;
         }
         uint32_t elementSize = selfModifyingStub ? sectInfo.reserved2 : ptrSize;
-        uint32_t elementCount = (uint32_t)(sectInfo.size/elementSize);
+        uint32_t elementCount = (uint32_t)(sectInfo.sectSize/elementSize);
         if ( greaterThanAddOrOverflow(sectInfo.reserved1, elementCount, indirectSymbolTableCount) ) {
-            diag.error("section %.*s overflows indirect symbol table", (int)sectInfo.sectionName.size(), sectInfo.sectionName.data());
+            diag.error("section %s overflows indirect symbol table", sectInfo.sectName);
             sectionStop = true;
             return;
         }
@@ -1221,7 +1227,7 @@
             if ( symNum == INDIRECT_SYMBOL_ABS )
                 continue;
             if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
-                handler(sectInfo.address+i*elementSize, false, 0, "", false, false, false, stop);
+                handler(sectInfo.sectAddr+i*elementSize, false, 0, "", false, false, false, stop);
                 continue;
             }
             if ( symNum > symCount ) {
@@ -1250,7 +1256,7 @@
                 // Note we only want to change the value in memory once, before rebases are applied.  We don't want to accidentally
                 // change it again later.
                 if ( supportPrivateExternsWorkaround ) {
-                    uintptr_t* ptr = (uintptr_t*)((uint8_t*)(sectInfo.address+i*elementSize) + this->getSlide());
+                    uintptr_t* ptr = (uintptr_t*)((uint8_t*)(sectInfo.sectAddr+i*elementSize) + this->getSlide());
                     uint64_t n_value = is64Bit ? symbols64[symNum].n_value : symbols32[symNum].n_value;
                     *ptr = (uintptr_t)n_value;
                 }
@@ -1260,7 +1266,7 @@
             // Handle defined weak def symbols which need to get a special ordinal
             if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) )
                 libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
-            handler(sectInfo.address+i*elementSize, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
+            handler(sectInfo.sectAddr+i*elementSize, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
         }
         sectionStop = stop;
     });
@@ -1290,7 +1296,7 @@
 
 bool MachOAnalyzer::validBindInfo(Diagnostics& diag, const char* path) const
 {
-    forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+    forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                          bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
                          uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
                          uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
@@ -1303,7 +1309,7 @@
     return diag.noError();
 }
 
-bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                                     bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint32_t ptrSize,
                                     uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const
 {
@@ -1315,8 +1321,8 @@
         diag.error("in '%s' %s segment index %d too large", path, opcodeName, segmentIndex);
         return true;
     }
-    if ( segmentOffset > (segments[segmentIndex].vmsize - ptrSize) ) {
-        diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmsize);
+    if ( segmentOffset > (segments[segmentIndex].vmSize-ptrSize) ) {
+        diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmSize);
         return true;
     }
     if ( symbolName == NULL ) {
@@ -1354,7 +1360,7 @@
             if ( isKextBundle() && (isArch("x86_64") || isArch("x86_64h")) )
                 forceAllowTextRelocs = true;
 #endif
-            if ( !forceAllowTextRelocs ) {
+            if ( !forceAllowTextRelocs && !segments[segmentIndex].textRelocs ) {
                 diag.error("in '%s' %s text bind is in segment that does not support text relocations", path, opcodeName);
                 return true;
             }
@@ -1381,20 +1387,20 @@
 {
     __block bool     startVmAddrSet = false;
     __block uint64_t startVmAddr    = 0;
-    forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+    forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                         bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
                         uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
                         uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
        if ( !startVmAddrSet ) {
             for (int i=0; i <= segmentIndex; ++i) {
-                if ( segments[i].segmentName == "__TEXT" ) {
-                    startVmAddr = segments[i].vmaddr;
+                if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+                    startVmAddr = segments[i].vmAddr;
                     startVmAddrSet = true;
                     break;
                }
             }
         }
-        uint64_t bindVmOffset  = segments[segmentIndex].vmaddr + segmentOffset;
+        uint64_t bindVmOffset  = segments[segmentIndex].vmAddr + segmentOffset;
         uint64_t runtimeOffset = bindVmOffset - startVmAddr;
         handler(runtimeOffset, libOrdinal, type, symbolName, weakImport, lazyBind, addend, stop);
     }, ^(const char* symbolName) {
@@ -1413,7 +1419,7 @@
 }
 
 void MachOAnalyzer::forEachBind(Diagnostics& diag,
-                                 void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const Header::SegmentInfo segments[],
+                                 void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
                                                  bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
                                                  uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type,
                                                  const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop),
@@ -1427,7 +1433,7 @@
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
@@ -1764,7 +1770,7 @@
                     else {
                         const char*     symbolName = stringPool + strOffset;
                         bool            weakImport = (n_desc & N_WEAK_REF);
-                        const uint8_t*  content    = (uint8_t*)this + segmentsInfo[segIndex].vmaddr - leInfo.layout.textUnslidVMAddr + segOffset;
+                        const uint8_t*  content    = (uint8_t*)this + segmentsInfo[segIndex].vmAddr - leInfo.layout.textUnslidVMAddr + segOffset;
                         uint64_t        addend     = (reloc->r_length == 3) ? *((uint64_t*)content) : *((uint32_t*)content);
                         // Handle defined weak def symbols which need to get a special ordinal
                         if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) )
@@ -1808,7 +1814,7 @@
     if ( diag.hasError() )
         return false;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return false;
@@ -1868,8 +1874,8 @@
         uint32_t numExtraSegments = (leInfo.layout.lastSegIndex + 1) - startsInfo->seg_count;
         for (unsigned i = 0; i != numExtraSegments; ++i) {
             // Check each extra segment before linkedit
-            const Header::SegmentInfo& segInfo = segmentsInfo[leInfo.layout.linkeditSegIndex - (i + 1)];
-            if ( segInfo.vmsize == 0 )
+            const SegmentInfo& segInfo = segmentsInfo[leInfo.layout.linkeditSegIndex - (i + 1)];
+            if ( segInfo.vmSize == 0 )
                 ++numNoRelocSegments;
         }
 
@@ -1878,7 +1884,7 @@
             return false;
         }
     }
-    const uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
+    const uint64_t baseAddress = preferredLoadAddress();
     uint32_t maxValidPointerSeen = 0;
     uint16_t pointer_format_for_all = 0;
     bool pointer_format_found = false;
@@ -1911,7 +1917,7 @@
             diag.error("chained fixups, pointer_format not same for all segments %d and %d", segInfo->pointer_format, pointer_format_for_all);
             return false;
         }
-        if ( segInfo->segment_offset != (segmentsInfo[i].vmaddr - baseAddress) ) {
+        if ( segInfo->segment_offset != (segmentsInfo[i].vmAddr - baseAddress) ) {
             diag.error("chained fixups, segment_offset does not match vmaddr from LC_SEGMENT in segment #%d", i);
             return false;
         }
@@ -1948,8 +1954,7 @@
                 uint16_t lastOffsetInPage = 0;
                 do {
                     if ( overflowIndex > maxOverflowIndex )  {
-                        diag.error("chain overflow index out of range %d (max=%d) in segment %.*s", overflowIndex, maxOverflowIndex,
-                                   (int)segmentName(i).size(), segmentName(i).data());
+                        diag.error("chain overflow index out of range %d (max=%d) in segment %s", overflowIndex, maxOverflowIndex, segmentName(i));
                         return false;
                     }
                     offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
@@ -1994,7 +1999,7 @@
 
     // validate max_valid_pointer is larger than last segment
     if ( (maxValidPointerSeen != 0) && !inDyldCache() ) {
-        uint64_t lastSegmentLastVMAddr = segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmaddr + segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmsize;
+        uint64_t lastSegmentLastVMAddr = segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmAddr + segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmSize;
         if ( maxValidPointerSeen < lastSegmentLastVMAddr ) {
             diag.error("chained fixups, max_valid_pointer too small for image");
             return false;
@@ -2012,7 +2017,7 @@
         ^(uint32_t totalTargets, bool& stop) {
             maxTargetCount = totalTargets;
         },
-        ^(const LinkEditInfo& leInfo, const Header::SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+        ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
            if ( symbolName == NULL ) {
                 diag.error("in '%s' missing BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path);
             }
@@ -2035,15 +2040,15 @@
             if ( diag.hasError() )
                 stop = true;
         },
-        ^(const LinkEditInfo& leInfo, const Header::SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop) {
+        ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop) {
            if ( !segIndexSet ) {
                 diag.error("in '%s' missing BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path);
             }
             else if ( segmentIndex >= leInfo.layout.linkeditSegIndex )  {
                 diag.error("in '%s' segment index %d too large", path, segmentIndex);
             }
-            else if ( segmentOffset > (segments[segmentIndex].vmsize-8) ) {
-                diag.error("in '%s' current segment offset 0x%08llX beyond segment size (0x%08llX)", path, segmentOffset, segments[segmentIndex].vmsize);
+            else if ( segmentOffset > (segments[segmentIndex].vmSize-8) ) {
+                diag.error("in '%s' current segment offset 0x%08llX beyond segment size (0x%08llX)", path, segmentOffset, segments[segmentIndex].vmSize);
             }
             else if ( !segments[segmentIndex].writable() ) {
                 diag.error("in '%s' pointer bind is in non-writable segment", path);
@@ -2062,8 +2067,8 @@
 
 
 void MachOAnalyzer::parseOrgArm64eChainedFixups(Diagnostics& diag, void (^targetCount)(uint32_t totalTargets, bool& stop),
-                                                                   void (^addTarget)(const LinkEditInfo& leInfo, const Header::SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
-                                                                   void (^addChainStart)(const LinkEditInfo& leInfo, const Header::SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop)) const
+                                                                   void (^addTarget)(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
+                                                                   void (^addChainStart)(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop)) const
 {
     bool            stop    = false;
 
@@ -2072,7 +2077,7 @@
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
@@ -2178,13 +2183,13 @@
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
 
     if ( leInfo.dyldInfo != nullptr ) {
-        parseOrgArm64eChainedFixups(diag, nullptr, ^(const LinkEditInfo& leInfo2, const Header::SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount,
+        parseOrgArm64eChainedFixups(diag, nullptr, ^(const LinkEditInfo& leInfo2, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount,
                                                     int libOrdinal, uint8_t type, const char* symbolName, uint64_t fixAddend, bool weakImport, bool& stopChain) {
             callback(libOrdinal, symbolName, fixAddend, weakImport, stopChain);
         }, nullptr);
@@ -2195,6 +2200,47 @@
     }
 }
 
+bool MachOAnalyzer::hasProgramVars(uint32_t& progVarsOffset, bool& crtRunsInitializers, DyldLookFunc*& dyldLookupFuncAddr) const
+{
+    crtRunsInitializers = false;
+    if ( this->filetype != MH_EXECUTE )
+        return false;
+
+    // macOS 10.8+              program uses LC_MAIN and ProgramVars are in libdyld.dylib
+    // macOS 10.6 -> 10.7       ProgramVars are in __program_vars section in main executable
+    // macOS 10.5               ProgramVars are in __dyld section in main executable and 7 pointers in size
+    // macOS 10.4 and earlier   ProgramVars need to be looked up by name in nlist of main executable
+
+    uint64_t offset;
+    bool     usesCRT;
+    if ( getEntry(offset, usesCRT) && usesCRT ) {
+        // is pre-10.8 program
+        uint64_t sectionSize;
+        if ( const void* progVarsSection = findSectionContent("__DATA", "__program_vars", sectionSize) ) {
+            // macOS 10.6 or 10.7 binary
+            progVarsOffset = (uint32_t)((uint8_t*)progVarsSection - (uint8_t*)this);
+            return true;
+        }
+        else if ( const void* dyldSection = findSectionContent("__DATA", "__dyld", sectionSize) ) {
+#if SUPPPORT_PRE_LC_MAIN
+            if ( sectionSize >= 2*pointerSize() ) {
+                dyldLookupFuncAddr = (DyldLookFunc*)((uint8_t*)dyldSection + pointerSize());
+            }
+#endif
+            if ( sectionSize >= 7*pointerSize() ) {
+                // macOS 10.5 binary
+                progVarsOffset = (uint32_t)((uint8_t*)dyldSection - (uint8_t*)this) + 2*pointerSize();
+                return true;
+            }
+            else {
+                // macOS 10.4 binary
+                crtRunsInitializers = true;
+            }
+        }
+        return false;
+    }
+    return false;
+}
 
 // Convert from a (possibly) live pointer to a vmAddr
 uint64_t MachOAnalyzer::VMAddrConverter::convertToVMAddr(uint64_t value, const Array<uint64_t>& bindTargets) const {
@@ -2272,18 +2318,6 @@
                 }
                 break;
             }
-            case VMAddrConverter::SharedCacheFormat::v5: {
-                // Just use the chained pointer format for arm64e
-                if ( value == 0 )
-                    return 0;
-                auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value;
-                uint64_t targetRuntimeOffset;
-                if ( chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E_SHARED_CACHE, preferredLoadAddress,
-                                            targetRuntimeOffset) ) {
-                    value = preferredLoadAddress + targetRuntimeOffset;
-                }
-                break;
-            }
         }
         return value;
     }
@@ -2299,7 +2333,7 @@
 
 MachOAnalyzer::VMAddrConverter MachOAnalyzer::makeVMAddrConverter(bool contentRebased) const {
     MachOAnalyzer::VMAddrConverter vmAddrConverter;
-    vmAddrConverter.preferredLoadAddress   = ((const Header*)this)->preferredLoadAddress();
+    vmAddrConverter.preferredLoadAddress   = preferredLoadAddress();
     vmAddrConverter.slide                  = getSlide();
     vmAddrConverter.chainedPointerFormat   = hasChainedFixups() ? chainedPointerFormat() : 0;
     vmAddrConverter.contentRebased         = contentRebased;
@@ -2333,9 +2367,9 @@
 void MachOAnalyzer::forEachInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset), const void* dyldCache) const
 {
     __block SegmentRanges executableSegments;
-    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-        if ( (info.initProt & VM_PROT_EXECUTE) != 0 ) {
-            executableSegments.segments.push_back({ info.vmaddr, info.vmaddr + info.vmsize, (uint32_t)info.fileSize });
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( (info.protections & VM_PROT_EXECUTE) != 0 ) {
+            executableSegments.segments.push_back({ info.vmAddr, info.vmAddr + info.vmSize, (uint32_t)info.fileSize });
         }
     });
 
@@ -2344,7 +2378,7 @@
         return;
     }
 
-    uint64_t loadAddress = ((const Header*)this)->preferredLoadAddress();
+    uint64_t loadAddress = preferredLoadAddress();
     intptr_t slide = getSlide();
 
     // if dylib linked with -init linker option, that initializer is first
@@ -2399,33 +2433,32 @@
         }
     });
 
-    forEachSection(^(const Header::SectionInfo& info, bool& stop) {
-        if ( (info.flags & SECTION_TYPE) != S_INIT_FUNC_OFFSETS )
+    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+        if ( (info.sectFlags & SECTION_TYPE) != S_INIT_FUNC_OFFSETS )
             return;
-        const uint8_t* content = (uint8_t*)(info.address + slide);
-        if ( info.segInitProt & VM_PROT_WRITE ) {
-            diag.error("initializer offsets section %.*s/%.*s must be in read-only segment",
-                       (int)info.segmentName.size(), info.segmentName.data(),
-                       (int)info.sectionName.size(), info.sectionName.data());
+        const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
+        if ( info.segInfo.writable() ) {
+            diag.error("initializer offsets section %s/%s must be in read-only segment", info.segInfo.segName, info.sectName);
             stop = true;
             return;
         }
-        if ( (info.size % 4) != 0 ) {
-            diag.error("initializer offsets section %.*s/%.*s has bad size",
-                       (int)info.segmentName.size(), info.segmentName.data(),
-                       (int)info.sectionName.size(), info.sectionName.data());
+        if ( (info.sectSize % 4) != 0 ) {
+            diag.error("initializer offsets section %s/%s has bad size", info.segInfo.segName, info.sectName);
             stop = true;
             return;
         }
-        if ( (info.address % 4) != 0 ) {
-            diag.error("initializer offsets section %.*s/%.*s is not 4-byte aligned",
-                       (int)info.segmentName.size(), info.segmentName.data(),
-                       (int)info.sectionName.size(), info.sectionName.data());
+        if ( malformedSectionRange ) {
+            diag.error("initializer offsets section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
             stop = true;
             return;
         }
+        if ( (info.sectAddr % 4) != 0 ) {
+            diag.error("initializer offsets section %s/%s is not 4-byte aligned", info.segInfo.segName, info.sectName);
+            stop = true;
+            return;
+        }
         const uint32_t* initsStart = (uint32_t*)content;
-        const uint32_t* initsEnd   = (uint32_t*)((uint8_t*)content + info.size);
+        const uint32_t* initsEnd   = (uint32_t*)((uint8_t*)content + info.sectSize);
         for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
             uint32_t anInitOffset = *p;
             if ( !executableSegments.contains(loadAddress + anInitOffset) ) {
@@ -2450,9 +2483,9 @@
 void MachOAnalyzer::forEachTerminator(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset)) const
 {
     __block SegmentRanges executableSegments;
-    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-        if ( (info.initProt & VM_PROT_EXECUTE) != 0 ) {
-            executableSegments.segments.push_back({ info.vmaddr, info.vmaddr + info.vmsize, (uint32_t)info.fileSize });
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( (info.protections & VM_PROT_EXECUTE) != 0 ) {
+            executableSegments.segments.push_back({ info.vmAddr, info.vmAddr + info.vmSize, (uint32_t)info.fileSize });
         }
     });
 
@@ -2461,32 +2494,33 @@
         return;
     }
 
-    uint64_t loadAddress = ((const Header*)this)->preferredLoadAddress();
+    uint64_t loadAddress = preferredLoadAddress();
     intptr_t slide = getSlide();
 
     // next any function pointers in mod-term section
     const unsigned ptrSize          = pointerSize();
-    forEachSection(^(const Header::SectionInfo& info, bool& stop) {
-        if ( (info.flags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS ) {
+    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+        if ( (info.sectFlags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS ) {
             const uint8_t* content;
-            content = (uint8_t*)(info.address + slide);
-            if ( (info.size % ptrSize) != 0 ) {
-                diag.error("terminator section %.*s/%.*s has bad size",
-                           (int)info.segmentName.size(), info.segmentName.data(),
-                           (int)info.sectionName.size(), info.sectionName.data());
+            content = (uint8_t*)(info.sectAddr + slide);
+            if ( (info.sectSize % ptrSize) != 0 ) {
+                diag.error("terminator section %s/%s has bad size", info.segInfo.segName, info.sectName);
                 stop = true;
                 return;
             }
-            if ( ((long)content % ptrSize) != 0 ) {
-                diag.error("terminator section %.*s/%.*s is not pointer aligned",
-                           (int)info.segmentName.size(), info.segmentName.data(),
-                           (int)info.sectionName.size(), info.sectionName.data());
+            if ( malformedSectionRange ) {
+                diag.error("terminator section %s/%s extends beyond its segment", info.segInfo.segName, info.sectName);
                 stop = true;
                 return;
             }
+            if ( ((long)content % ptrSize) != 0 ) {
+                diag.error("terminator section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
+                stop = true;
+                return;
+            }
             if ( ptrSize == 8 ) {
                 const uint64_t* initsStart = (uint64_t*)content;
-                const uint64_t* initsEnd   = (uint64_t*)((uint8_t*)content + info.size);
+                const uint64_t* initsEnd   = (uint64_t*)((uint8_t*)content + info.sectSize);
                 for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
                     uint64_t rawContent = *p;
     #if __has_feature(ptrauth_calls)
@@ -2503,7 +2537,7 @@
             }
             else {
                 const uint32_t* initsStart = (uint32_t*)content;
-                const uint32_t* initsEnd   = (uint32_t*)((uint8_t*)content + info.size);
+                const uint32_t* initsEnd   = (uint32_t*)((uint8_t*)content + info.sectSize);
                 for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
                     uint32_t anInit = (uint32_t)vmAddrConverter.convertToVMAddr(*p);
                     if ( !executableSegments.contains(anInit) ) {
@@ -2530,10 +2564,10 @@
 
     uintptr_t slide = getSlide();
     __block bool result = false;
-    forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
-        if ( (sectInfo.sectionName == "__objc_imageinfo") && sectInfo.segmentName.starts_with("__DATA") ) {
+    forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+        if ( (strcmp(sectInfo.sectName, "__objc_imageinfo") == 0) && (strncmp(sectInfo.segInfo.segName, "__DATA", 6) == 0) ) {
             if ( hasSwift != nullptr ) {
-                objc_image_info* info =  (objc_image_info*)((uintptr_t)sectInfo.address + slide);
+                objc_image_info* info =  (objc_image_info*)((uintptr_t)sectInfo.sectAddr + slide);
                 uint32_t swiftVersion = ((info->flags >> 8) & 0xFF);
                 if ( swiftVersion )
                      *hasSwift = true;
@@ -2542,7 +2576,7 @@
             result = true;
             stop = true;
         }
-        if ( (this->cputype == CPU_TYPE_I386) && (sectInfo.sectionName == "__image_info") && (sectInfo.sectionName == "__OBJC") ) {
+        if ( (this->cputype == CPU_TYPE_I386) && (strcmp(sectInfo.sectName, "__image_info") == 0) && (strcmp(sectInfo.segInfo.segName, "__OBJC") == 0) ) {
             result = true;
             stop = true;
         }
@@ -2560,16 +2594,51 @@
 bool MachOAnalyzer::usesObjCGarbageCollection() const
 {
     __block bool result = false;
-    forEachSection(^(const Header::SectionInfo& info, bool& stop) {
-        if ( (info.sectionName == "__objc_imageinfo") && info.segmentName.starts_with("__DATA") ) {
-            const uint64_t  slide = (uint64_t)this - ((const Header*)this)->preferredLoadAddress();
-            const uint32_t* flags = (uint32_t*)(info.address + slide);
+    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+        if ( (strcmp(info.sectName, "__objc_imageinfo") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
+            const uint64_t  slide = (uint64_t)this - preferredLoadAddress();
+            const uint32_t* flags = (uint32_t*)(info.sectAddr + slide);
             if ( flags[1] & 4 )
                 result = true;
             stop = true;
         }
      });
     return result;
+}
+
+
+bool MachOAnalyzer::hasPlusLoadMethod(Diagnostics& diag) const
+{
+    __block bool result = false;
+    if ( (this->cputype == CPU_TYPE_I386) && this->builtForPlatform(Platform::macOS) ) {
+        // old objc runtime has no special section for +load methods, scan for string
+        uintptr_t slide = getSlide();
+        forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+            if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
+                if ( malformedSectionRange ) {
+                    diag.error("cstring section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
+                    stop = true;
+                    return;
+                }
+                const uint8_t* content = (uint8_t*)((uintptr_t)info.sectAddr + slide);
+                const char* s   = (char*)content;
+                const char* end = s + info.sectSize;
+                while ( s < end ) {
+                    if ( strcmp(s, "load") == 0 ) {
+                        result = true;
+                        stop = true;
+                        return;
+                    }
+                    while (*s != '\0' )
+                        ++s;
+                    ++s;
+                }
+            }
+        });
+        return result;
+    }
+
+    return MachOFile::hasPlusLoadMethod(diag);
 }
 
 const void* MachOAnalyzer::getRebaseOpcodes(uint32_t& size) const
@@ -2649,11 +2718,11 @@
 {
     __block uint64_t textVmAddr = 0;
     __block uint64_t result     = 0;
-    ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& info, bool& stop) {
-        if ( info.segmentName == "__TEXT" )
-            textVmAddr = info.vmaddr;
-        if ( info.segmentIndex == targetSegIndex ) {
-            result = (info.vmaddr - textVmAddr) + targetSegOffset;
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 )
+            textVmAddr = info.vmAddr;
+        if ( info.segIndex == targetSegIndex ) {
+            result = (info.vmAddr - textVmAddr) + targetSegOffset;
         }
     });
     return result;
@@ -2662,10 +2731,10 @@
 bool MachOAnalyzer::hasLazyPointers(uint32_t& runtimeOffset, uint32_t& size) const
 {
     size = 0;
-    forEachSection(^(const Header::SectionInfo& info, bool &stop) {
-        if ( (info.flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
-            runtimeOffset = (uint32_t)(info.address - ((const Header*)this)->preferredLoadAddress());
-            size          = (uint32_t)info.size;
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( (info.sectFlags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
+            runtimeOffset = (uint32_t)(info.sectAddr - preferredLoadAddress());
+            size          = (uint32_t)info.sectSize;
             stop = true;
         }
     });
@@ -2706,6 +2775,60 @@
     return requiresLV;
 }
 #endif // !TARGET_OS_EXCLAVEKIT
+
+bool MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const
+{
+    if (!MachOFile::canHavePrecomputedDlopenClosure(path, failureReason))
+        return false;
+
+    // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same
+    // at runtime as when the shared cache processed it.  We must have a code signature to record this information
+    uint32_t codeSigFileOffset;
+    uint32_t codeSigSize;
+    if ( !hasCodeSignature(codeSigFileOffset, codeSigSize) ) {
+        failureReason("no code signature");
+        return false;
+    }
+
+    __block bool retval = true;
+
+    // images that use dynamic_lookup, bundle_loader, or have weak-defs cannot have dlopen closure pre-computed
+    Diagnostics diag;
+    auto checkBind = ^(int libOrdinal, bool& stop) {
+        switch (libOrdinal) {
+            case BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
+                failureReason("has weak externals");
+                retval = false;
+                stop = true;
+                break;
+            case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
+                failureReason("has dynamic_lookup binds");
+                retval = false;
+                stop = true;
+                break;
+            case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
+                failureReason("has reference to main executable (bundle loader)");
+                retval = false;
+                stop = true;
+                break;
+        }
+    };
+
+    if (hasChainedFixups()) {
+        forEachChainedFixupTarget(diag, ^(int libOrdinal, const char *symbolName, uint64_t addend, bool weakImport, bool &stop) {
+            checkBind(libOrdinal, stop);
+        });
+    } else {
+        forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
+            checkBind(libOrdinal, stop);
+        },
+        ^(const char* symbolName) {
+        });
+    }
+
+    return retval;
+}
+
 
 bool MachOAnalyzer::hasUnalignedPointerFixups() const
 {
@@ -2837,7 +2960,7 @@
         }
     }
 
-    if ( (this->flags & MH_HAS_TLV_DESCRIPTORS) ) {
+    if ( this->hasThreadLocalVariables() ) {
         return true;
     }
     else {
@@ -2883,17 +3006,17 @@
         __block bool foundHIB = false;
         __block uint64_t hibernateVMAddr = 0;
         __block uint64_t hibernateVMSize = 0;
-        ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo &segmentInfo, bool &stop) {
-            if ( segmentInfo.segmentName == "__TEXT" ) {
+        forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) {
+            if ( strcmp(segmentInfo.segName, "__TEXT") == 0 ) {
                 foundText = true;
             }
-            if ( segmentInfo.segmentName == "__TEXT_EXEC" ) {
+            if ( strcmp(segmentInfo.segName, "__TEXT_EXEC") == 0 ) {
                 foundTextExec = true;
             }
-            if ( segmentInfo.segmentName == "__HIB" ) {
+            if ( strcmp(segmentInfo.segName, "__HIB") == 0 ) {
                 foundHIB = true;
-                hibernateVMAddr = segmentInfo.vmaddr;
-                hibernateVMSize = segmentInfo.vmsize;
+                hibernateVMAddr = segmentInfo.vmAddr;
+                hibernateVMSize = segmentInfo.vmSize;
             }
         });
         if (!foundText) {
@@ -2911,7 +3034,7 @@
 
         // The hibernate segment should be mapped before the base address
         if ( foundHIB ) {
-            uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
+            uint64_t baseAddress = preferredLoadAddress();
             if ( greaterThanAddOrOverflow(hibernateVMAddr, hibernateVMSize, baseAddress) ) {
                 failureReason("__HIB segment should be mapped before base address");
                 return false;
@@ -2920,7 +3043,7 @@
     }
 
     // Don't allow kext's to have load addresses
-    if ( isKextBundle() && (((const Header*)this)->preferredLoadAddress() != 0) ) {
+    if ( isKextBundle() && (preferredLoadAddress() != 0) ) {
         failureReason("Has load address");
         return false;
     }
@@ -3033,10 +3156,10 @@
             // range of the number of bits we have
             if ( isStaticExecutable() ) {
                 __block uint64_t baseAddress = ~0ULL;
-                ((const Header*)this)->forEachSegment(^(const Header::SegmentInfo& sinfo, bool& segStop) {
-                    baseAddress = std::min(baseAddress, sinfo.vmaddr);
+                forEachSegment(^(const SegmentInfo& sinfo, bool& segStop) {
+                    baseAddress = std::min(baseAddress, sinfo.vmAddr);
                 });
-                uint64_t textSegVMAddr = ((const Header*)this)->preferredLoadAddress();
+                uint64_t textSegVMAddr = preferredLoadAddress();
                 runtimeOffset = (textSegVMAddr + runtimeOffset) - baseAddress;
             }
 #endif
@@ -3147,7 +3270,7 @@
     // don't want this code in non-arm64e dyld because it causes a stack protector which dereferences a GOT pointer before GOT is set up
     else if ( (leInfo.dyldInfo != nullptr) && (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
         // old arm64e binary, create a dyld_chained_starts_in_image for caller
-        uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
+        uint64_t baseAddress = preferredLoadAddress();
         uint64_t imagePageCount = this->mappedSize()/0x1000;
         size_t bufferSize = leInfo.dyldInfo->bind_size + (size_t)imagePageCount*sizeof(uint16_t) + 512;
         BLOCK_ACCCESSIBLE_ARRAY(uint8_t, buffer, bufferSize);
@@ -3158,7 +3281,7 @@
             header->seg_info_offset[i] = 0;
         __block uint8_t curSegIndex = 0;
         __block dyld_chained_starts_in_segment* curSeg = (dyld_chained_starts_in_segment*)(&(header->seg_info_offset[header->seg_count]));
-        parseOrgArm64eChainedFixups(diag, nullptr, nullptr, ^(const LinkEditInfo& leInfo2, const Header::SegmentInfo segments[], uint8_t segmentIndex,
+        parseOrgArm64eChainedFixups(diag, nullptr, nullptr, ^(const LinkEditInfo& leInfo2, const SegmentInfo segments[], uint8_t segmentIndex,
                                                               bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop) {
             uint32_t pageIndex = (uint32_t)(segmentOffset/0x1000);
             if ( segmentIndex != curSegIndex ) {
@@ -3181,7 +3304,7 @@
             curSeg->size                  = (uint32_t)((uint8_t*)(&curSeg->page_start[pageIndex]) - (uint8_t*)curSeg);
             curSeg->page_size             = 0x1000; // old arm64e encoding used 4KB pages
             curSeg->pointer_format        = DYLD_CHAINED_PTR_ARM64E;
-            curSeg->segment_offset        = segments[segmentIndex].vmaddr - baseAddress;
+            curSeg->segment_offset        = segments[segmentIndex].vmAddr - baseAddress;
             curSeg->max_valid_pointer     = 0;
             curSeg->page_count            = pageIndex+1;
             assert((uint8_t*)(&curSeg->page_start[pageIndex]) < bufferEnd);
@@ -3310,22 +3433,22 @@
     result.protocolDefCount = 0;
 
     const uint32_t ptrSize  = pointerSize();
-    forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
-        if ( sectInfo.segmentName.starts_with("__DATA") ) {
-            if ( sectInfo.sectionName == "__objc_selrefs" )
-                result.selRefCount += (sectInfo.size/ptrSize);
-            else if ( sectInfo.sectionName == "__objc_classlist" )
-                result.classDefCount += (sectInfo.size/ptrSize);
-            else if ( sectInfo.sectionName == "__objc_protolist" )
-                result.protocolDefCount += (sectInfo.size/ptrSize);
-        }
-        else if ( (this->cputype == CPU_TYPE_I386) && (sectInfo.sectionName == "__OBJC") ) {
-            if ( sectInfo.sectionName == "__message_refs" )
-                result.selRefCount += (sectInfo.size/4);
-            else if ( sectInfo.sectionName == "__class" )
-                result.classDefCount += (sectInfo.size/48);
-            else if ( sectInfo.sectionName == "__protocol" )
-                result.protocolDefCount += (sectInfo.size/20);
+    forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+        if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) == 0 ) {
+            if ( strcmp(sectInfo.sectName, "__objc_selrefs") == 0 )
+                result.selRefCount += (sectInfo.sectSize/ptrSize);
+            else if ( strcmp(sectInfo.sectName, "__objc_classlist") == 0 )
+                result.classDefCount += (sectInfo.sectSize/ptrSize);
+            else if ( strcmp(sectInfo.sectName, "__objc_protolist") == 0 )
+                result.protocolDefCount += (sectInfo.sectSize/ptrSize);
+        }
+        else if ( (this->cputype == CPU_TYPE_I386) && (strcmp(sectInfo.segInfo.segName, "__OBJC") == 0) ) {
+            if ( strcmp(sectInfo.sectName, "__message_refs") == 0 )
+                result.selRefCount += (sectInfo.sectSize/4);
+            else if ( strcmp(sectInfo.sectName, "__class") == 0 )
+                result.classDefCount += (sectInfo.sectSize/48);
+            else if ( strcmp(sectInfo.sectName, "__protocol") == 0 )
+                result.protocolDefCount += (sectInfo.sectSize/20);
         }
    });
 
@@ -3406,32 +3529,32 @@
     uint32_t fairplayTextOffsetStart;
     uint32_t fairplayTextOffsetEnd;
     uint32_t fairplaySize;
-    if ( ((const Header*)this)->isFairPlayEncrypted(fairplayTextOffsetStart, fairplaySize) ) {
+    if ( isFairPlayEncrypted(fairplayTextOffsetStart, fairplaySize) ) {
         fairplayTextOffsetEnd = fairplayTextOffsetStart + fairplaySize;
     } else {
         fairplayTextOffsetEnd = 0;
     }
 
     result = PrintableStringResult::UnknownSection;
-    forEachSection(^(const Header::SegmentInfo& segInfo, const Header::SectionInfo &sectInfo, bool &stop) {
-        if ( stringVMAddr < sectInfo.address ) {
+    forEachSection(^(const MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
+        if ( stringVMAddr < sectInfo.sectAddr ) {
             return;
         }
-        if ( stringVMAddr >= ( sectInfo.address + sectInfo.size) ) {
+        if ( stringVMAddr >= ( sectInfo.sectAddr + sectInfo.sectSize) ) {
             return;
         }
 
         // We can't scan this section if its protected
-        if ( segInfo.isProtected() ) {
+        if ( sectInfo.segInfo.isProtected ) {
             result = PrintableStringResult::ProtectedSection;
             stop = true;
             return;
         }
 
         // We can't scan this section if it overlaps with the fairplay range
-        if ( fairplayTextOffsetEnd < sectInfo.fileOffset ) {
+        if ( fairplayTextOffsetEnd < sectInfo.sectFileOffset ) {
             // Fairplay range ends before section
-        } else if ( fairplayTextOffsetStart > (sectInfo.fileOffset + sectInfo.size) ) {
+        } else if ( fairplayTextOffsetStart > (sectInfo.sectFileOffset + sectInfo.sectSize) ) {
             // Fairplay range starts after section
         } else {
             // Must overlap
@@ -3467,7 +3590,7 @@
     STACK_ALLOC_OVERFLOW_SAFE_ARRAY(uint64_t, bindTargets, 32);
     if ( this->hasChainedFixups() ) {
         intptr_t slide = this->getSlide();
-        __block Diagnostics diag;
+        Diagnostics diag;
         this->forEachBindTarget(diag, false, ^(const BindTargetInfo& info, bool& stop) {
             if ( diag.hasError() ) {
                 stop = true;
@@ -3545,7 +3668,7 @@
                                      ClassCallback& callback) const {
     uint64_t classListRuntimeOffset;
     uint64_t classListSize;
-    bool foundSection = ((const Header*)this)->findObjCDataSection("__objc_classlist", classListRuntimeOffset, classListSize);
+    bool foundSection = findObjCDataSection("__objc_classlist", classListRuntimeOffset, classListSize);
     if ( !foundSection )
         return;
 
@@ -3740,7 +3863,7 @@
                                         CategoryCallback& callback) const {
     uint64_t categoryListRuntimeOffset;
     uint64_t categoryListSize;
-    bool foundSection = ((const Header*)this)->findObjCDataSection("__objc_catlist", categoryListRuntimeOffset, categoryListSize);
+    bool foundSection = findObjCDataSection("__objc_catlist", categoryListRuntimeOffset, categoryListSize);
     if ( !foundSection )
         return;
 
@@ -3835,7 +3958,7 @@
                                         ProtocolCallback& callback) const {
     uint64_t protocolListRuntimeOffset;
     uint64_t protocolListSize;
-    bool foundSection = ((const Header*)this)->findObjCDataSection("__objc_protolist", protocolListRuntimeOffset, protocolListSize);
+    bool foundSection = findObjCDataSection("__objc_protolist", protocolListRuntimeOffset, protocolListSize);
     if ( !foundSection )
         return;
 
@@ -4222,7 +4345,7 @@
 void MachOAnalyzer::forEachObjCSelectorReference(uint64_t selRefsRuntimeOffset, uint64_t selRefsCount, const VMAddrConverter& vmAddrConverter,
                                                  void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr, bool& stop)) const
 {
-    uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
+    uint64_t baseAddress = preferredLoadAddress();
     const uint64_t ptrSize = pointerSize();
     const uint8_t* selRefs = (uint8_t*)this + selRefsRuntimeOffset;
     if ( ptrSize == 8 ) {
@@ -4254,7 +4377,7 @@
 {
     uint64_t selRefsRuntimeOffset;
     uint64_t selRefsSize;
-    bool foundSection = ((const Header*)this)->findObjCDataSection("__objc_selrefs", selRefsRuntimeOffset, selRefsSize);
+    bool foundSection = findObjCDataSection("__objc_selrefs", selRefsRuntimeOffset, selRefsSize);
     if ( !foundSection )
         return;
 
@@ -4269,18 +4392,22 @@
 
 void MachOAnalyzer::forEachObjCMethodName(void (^handler)(const char* methodName)) const {
     intptr_t slide = getSlide();
-    forEachSection(^(const Header::SegmentInfo& segInfo, const Header::SectionInfo& sectInfo, bool& stop) {
-        if ( sectInfo.sectionName != "__TEXT" )
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+        if ( strcmp(sectInfo.segInfo.segName, "__TEXT") != 0 )
             return;
-        if ( sectInfo.sectionName != "__objc_methname" )
+        if ( strcmp(sectInfo.sectName, "__objc_methname") != 0 )
             return;
-        if ( segInfo.isProtected() || ( (sectInfo.flags & SECTION_TYPE) != S_CSTRING_LITERALS ) ) {
+        if ( sectInfo.segInfo.isProtected || ( (sectInfo.sectFlags & SECTION_TYPE) != S_CSTRING_LITERALS ) ) {
             stop = true;
             return;
         }
-
-        const char* content       = (const char*)(sectInfo.address + slide);
-        uint64_t    sectionSize   = sectInfo.size;
+        if ( malformedSectionRange ) {
+            stop = true;
+            return;
+        }
+
+        const char* content       = (const char*)(sectInfo.sectAddr + slide);
+        uint64_t    sectionSize   = sectInfo.sectSize;
 
         const char* s   = (const char*)content;
         const char* end = s + sectionSize;
@@ -4325,16 +4452,20 @@
 
     __block bool foundInvalidObjCImageInfo = false;
     __block const ObjCImageInfo* imageInfo = nullptr;
-    forEachSection(^(const Header::SectionInfo& sectionInfo, bool& stop) {
-        if ( !sectionInfo.segmentName.starts_with("__DATA") )
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& sectionInfo, bool malformedSectionRange, bool& stop) {
+        if ( strncmp(sectionInfo.segInfo.segName, "__DATA", 6) != 0 )
             return;
-        if ( sectionInfo.sectionName != "__objc_imageinfo" )
+        if (strcmp(sectionInfo.sectName, "__objc_imageinfo") != 0)
             return;
-        if ( sectionInfo.size != 8 ) {
+        if ( malformedSectionRange ) {
             stop = true;
             return;
         }
-        imageInfo = (const ObjCImageInfo*)((uintptr_t)sectionInfo.address + slide);
+        if ( sectionInfo.sectSize != 8 ) {
+            stop = true;
+            return;
+        }
+        imageInfo = (const ObjCImageInfo*)((uintptr_t)sectionInfo.sectAddr + slide);
         if ( (imageInfo->flags & ObjCImageInfo::dyldPreoptimized) != 0 ) {
             foundInvalidObjCImageInfo = true;
             stop = true;
@@ -4349,7 +4480,7 @@
 
 void MachOAnalyzer::forEachWeakDef(Diagnostics& diag,
                                    void (^handler)(const char* symbolName, uint64_t imageOffset, bool isFromExportTrie)) const {
-    uint64_t baseAddress = ((const Header*)this)->preferredLoadAddress();
+    uint64_t baseAddress = preferredLoadAddress();
     forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
         if ( (n_desc & N_WEAK_DEF) != 0 ) {
             handler(symbolName, n_value - baseAddress, false);
@@ -4367,6 +4498,55 @@
     });
 }
 
+template<typename P> void MachOAnalyzer::forEachThreadLocalVariableInSection(Diagnostics& diag, const MachOAnalyzer::SectionInfo& sectInfo, void (^handler)(TLV_ResolverPtr tlvThunkAddr, uintptr_t* keyAddr)) const {
+    uintptr_t  baseAddress = (uintptr_t)this->preferredLoadAddress();
+    intptr_t   slide       = (uintptr_t)this - baseAddress;
+    const uint8_t* content = (uint8_t*)((uintptr_t)sectInfo.sectAddr + slide);
+    unsigned count = (unsigned)(sectInfo.sectSize / sizeof(MachOAnalyzer::FixedSizeTLVThunk<P>));
+    FixedSizeTLVThunk<P>* slotsStart = (FixedSizeTLVThunk<P>*)content;
+    FixedSizeTLVThunk<P>* slotsEnd   =  &slotsStart[count];
+    for (FixedSizeTLVThunk<P>* p=slotsStart; p < slotsEnd; ++p) {
+        handler((TLV_ResolverPtr)&(p->thunk), (uintptr_t*)&(p->key));
+    }
+}
+
+MachOAnalyzer::TLV_InitialContent MachOAnalyzer::forEachThreadLocalVariable(Diagnostics& diag, void (^handler)(TLV_ResolverPtr tlvThunkAddr, uintptr_t* keyAddr)) const
+{
+    __block TLV_InitialContent result = { 0, 0 };
+
+    // most images don't use thread locals, so quickly skip those
+    if ( !this->hasThreadLocalVariables() )
+        return result;
+
+    uintptr_t  baseAddress = (uintptr_t)this->preferredLoadAddress();
+    this->forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+        switch (sectInfo.sectFlags & SECTION_TYPE) {
+            case S_THREAD_LOCAL_VARIABLES:
+                {
+                    if (is64()) {
+                        forEachThreadLocalVariableInSection<int64_t>(diag, sectInfo, handler);
+                    } else {
+                        forEachThreadLocalVariableInSection<int32_t>(diag, sectInfo, handler);
+                    }
+                }
+                break;
+            case S_THREAD_LOCAL_ZEROFILL:
+            case S_THREAD_LOCAL_REGULAR:
+                if ( result.runtimeOffset == 0 ) {
+                    // first of N contiguous TLV template sections, record as if this was only section
+                    result.runtimeOffset = sectInfo.sectAddr - baseAddress;
+                    result.size          = sectInfo.sectSize;
+                }
+                else {
+                    // non-first of N contiguous TLV template sections, accumlate values
+                    result.size = sectInfo.sectAddr + sectInfo.sectSize - baseAddress - result.runtimeOffset;
+                }
+                break;
+        }
+    });
+
+    return result;
+}
 
 void MachOAnalyzer::forEachBindTarget(Diagnostics& diag, bool allowLazyBinds,
                                       void (^handler)(const BindTargetInfo& info, bool& stop),
@@ -4400,7 +4580,7 @@
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
@@ -4408,12 +4588,12 @@
     {
         __block unsigned         targetIndex = 0;
         __block BindTargetInfo   targetInfo;
-        BindDetailedHandler binder =  ^(const char* opcodeName, const LinkEditInfo&, const Header::SegmentInfo segments[],
+        BindDetailedHandler binder =  ^(const char* opcodeName, const LinkEditInfo&, const SegmentInfo segments[],
                                         bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
                                         uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset,
                                         uint8_t type, const char* symbolName, bool weakImport, bool lazyBind,
                                         uint64_t addend, bool targetOrAddendChanged, bool& stop) {
-            uint64_t bindVmOffset  = segments[segmentIndex].vmaddr + segmentOffset;
+            uint64_t bindVmOffset  = segments[segmentIndex].vmAddr + segmentOffset;
             uint64_t runtimeOffset = bindVmOffset - leInfo.layout.textUnslidVMAddr;
             if ( targetOrAddendChanged ) {
                 targetInfo.targetIndex = targetIndex++;
@@ -4439,15 +4619,15 @@
     {
         __block unsigned         weakTargetIndex = 0;
         __block BindTargetInfo   weakTargetInfo;
-        BindDetailedHandler weakBinder =  ^(const char* opcodeName, const LinkEditInfo&, const Header::SegmentInfo segments[],
+        BindDetailedHandler weakBinder =  ^(const char* opcodeName, const LinkEditInfo&, const SegmentInfo segments[],
                                             bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
                                             uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset,
                                             uint8_t type, const char* symbolName, bool weakImport, bool lazyBind,
                                             uint64_t addend, bool targetOrAddendChanged, bool& stop) {
             
-            uint64_t bindVmOffset  = segmentsInfo[segmentIndex].vmaddr + segmentOffset;
+            uint64_t bindVmOffset  = segmentsInfo[segmentIndex].vmAddr + segmentOffset;
             uint64_t runtimeOffset = bindVmOffset - leInfo.layout.textUnslidVMAddr;
-            if ( (weakTargetIndex == 0) || (symbolName != weakTargetInfo.symbolName) || (strcmp(symbolName, weakTargetInfo.symbolName) != 0) || (weakTargetInfo.addend != addend) ) {
+            if ( (symbolName != weakTargetInfo.symbolName) || (strcmp(symbolName, weakTargetInfo.symbolName) != 0) || (weakTargetInfo.addend != addend) ) {
                 weakTargetInfo.targetIndex = weakTargetIndex++;
                 weakTargetInfo.libOrdinal  = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
                 weakTargetInfo.symbolName  = symbolName;
@@ -4527,14 +4707,14 @@
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
 
     __block unsigned targetIndex = 0;
     this->forEachBind_Relocations(diag, leInfo, segmentsInfo, true,
-                                  ^(const char* opcodeName, const LinkEditInfo&, const Header::SegmentInfo segments[],
+                                  ^(const char* opcodeName, const LinkEditInfo&, const SegmentInfo segments[],
                                     bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
                                     uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset,
                                     uint8_t type, const char* symbolName, bool weakImport, bool lazyBind,
@@ -4562,28 +4742,28 @@
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
 
     __block int targetIndex = -1;
     this->forEachBind_Relocations(diag, leInfo, segmentsInfo, false,
-                                  ^(const char* opcodeName, const LinkEditInfo&, const Header::SegmentInfo segments[],
+                                  ^(const char* opcodeName, const LinkEditInfo&, const SegmentInfo segments[],
                                     bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
                                     uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset,
                                     uint8_t type, const char* symbolName, bool weakImport, bool lazyBind,
                                     uint64_t addend, bool targetOrAddendChanged, bool& stop) {
         if ( targetOrAddendChanged )
             ++targetIndex;
-        uint64_t bindVmOffset  = segments[segmentIndex].vmaddr + segmentOffset;
+        uint64_t bindVmOffset  = segments[segmentIndex].vmAddr + segmentOffset;
         uint64_t runtimeOffset = bindVmOffset - leInfo.layout.textUnslidVMAddr;
         handler(runtimeOffset, targetIndex, stop);
     });
 }
 
 #if SUPPORT_CLASSIC_RELOCS
-bool MachOAnalyzer::forEachBind_Relocations(Diagnostics& diag, const LinkEditInfo& leInfo, const Header::SegmentInfo segmentsInfo[],
+bool MachOAnalyzer::forEachBind_Relocations(Diagnostics& diag, const LinkEditInfo& leInfo, const SegmentInfo segmentsInfo[],
                                             bool supportPrivateExternsWorkaround, BindDetailedHandler handler) const
 {
     // Firmare binaries won't have a dynSymTab
@@ -4656,7 +4836,7 @@
                 else {
                     const char*     symbolName = stringPool + strOffset;
                     bool            weakImport = (n_desc & N_WEAK_REF);
-                    const uint8_t*  content    = (uint8_t*)this + segmentsInfo[segIndex].vmaddr - leInfo.layout.textUnslidVMAddr + segOffset;
+                    const uint8_t*  content    = (uint8_t*)this + segmentsInfo[segIndex].vmAddr - leInfo.layout.textUnslidVMAddr + segOffset;
                     uint64_t        addend     = (reloc->r_length == 3) ? *((uint64_t*)content) : *((uint32_t*)content);
                     // Handle defined weak def symbols which need to get a special ordinal
                     if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) )
@@ -4707,7 +4887,7 @@
     });
 }
 
-bool MachOAnalyzer::forEachBind_OpcodesLazy(Diagnostics& diag, const LinkEditInfo& leInfo, const Header::SegmentInfo segmentsInfo[], BindDetailedHandler handler) const
+bool MachOAnalyzer::forEachBind_OpcodesLazy(Diagnostics& diag, const LinkEditInfo& leInfo, const SegmentInfo segmentsInfo[], BindDetailedHandler handler) const
 {
     if ( (leInfo.dyldInfo == nullptr) || (leInfo.dyldInfo->lazy_bind_size == 0) )
         return false;
@@ -4794,7 +4974,7 @@
 
 
 
-bool MachOAnalyzer::forEachBind_OpcodesWeak(Diagnostics& diag, const LinkEditInfo& leInfo, const Header::SegmentInfo segmentsInfo[], BindDetailedHandler handler,  void (^strongHandler)(const char* symbolName)) const
+bool MachOAnalyzer::forEachBind_OpcodesWeak(Diagnostics& diag, const LinkEditInfo& leInfo, const SegmentInfo segmentsInfo[], BindDetailedHandler handler,  void (^strongHandler)(const char* symbolName)) const
 {
    if ( (leInfo.dyldInfo == nullptr) || (leInfo.dyldInfo->weak_bind_size == 0) )
         return false;
@@ -4893,7 +5073,7 @@
     return stop;
 }
 
-bool MachOAnalyzer::forEachBind_OpcodesRegular(Diagnostics& diag, const LinkEditInfo& leInfo, const Header::SegmentInfo segmentsInfo[], BindDetailedHandler handler) const
+bool MachOAnalyzer::forEachBind_OpcodesRegular(Diagnostics& diag, const LinkEditInfo& leInfo, const SegmentInfo segmentsInfo[], BindDetailedHandler handler) const
 {
     if ( (leInfo.dyldInfo == nullptr) || (leInfo.dyldInfo->bind_size == 0) )
         return false;
@@ -5009,20 +5189,20 @@
     if ( diag.hasError() )
         return false;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return false;
 
-    return this->forEachRebase_Opcodes(diag, leInfo, segmentsInfo, ^(const char* opcodeName, const LinkEditInfo& rleInfo, const Header::SegmentInfo segments[],
+    return this->forEachRebase_Opcodes(diag, leInfo, segmentsInfo, ^(const char* opcodeName, const LinkEditInfo& rleInfo, const SegmentInfo segments[],
                                         bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
-        uint64_t rebaseVmOffset = segments[segmentIndex].vmaddr + segmentOffset;
+        uint64_t rebaseVmOffset = segments[segmentIndex].vmAddr + segmentOffset;
         uint64_t runtimeOffset  = rebaseVmOffset - leInfo.layout.textUnslidVMAddr;
         handler(runtimeOffset, stop);
     });
 }
 
-bool MachOAnalyzer::forEachRebase_Opcodes(Diagnostics& diag, const LinkEditInfo& leInfo, const Header::SegmentInfo segmentsInfo[], RebaseDetailHandler handler) const
+bool MachOAnalyzer::forEachRebase_Opcodes(Diagnostics& diag, const LinkEditInfo& leInfo, const SegmentInfo segmentsInfo[], RebaseDetailHandler handler) const
 {
     const Rebase pointerRebaseKind = is64() ? Rebase::pointer64 : Rebase::pointer32;
     assert(leInfo.dyldInfo != nullptr);
@@ -5124,14 +5304,14 @@
     if ( diag.hasError() )
         return false;
 
-    BLOCK_ACCCESSIBLE_ARRAY(Header::SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return false;
 
-    return this->forEachRebase_Relocations(diag, leInfo, segmentsInfo, ^(const char* opcodeName, const LinkEditInfo& rleInfo, const Header::SegmentInfo segments[],
+    return this->forEachRebase_Relocations(diag, leInfo, segmentsInfo, ^(const char* opcodeName, const LinkEditInfo& rleInfo, const SegmentInfo segments[],
                                            bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
-        uint64_t rebaseVmOffset = segments[segmentIndex].vmaddr + segmentOffset;
+        uint64_t rebaseVmOffset = segments[segmentIndex].vmAddr + segmentOffset;
         uint64_t runtimeOffset  = rebaseVmOffset - leInfo.layout.textUnslidVMAddr;
         handler(runtimeOffset, stop);
     });
@@ -5168,7 +5348,7 @@
 }
 
 
-bool MachOAnalyzer::forEachRebase_Relocations(Diagnostics& diag, const LinkEditInfo& leInfo, const Header::SegmentInfo segmentsInfo[], RebaseDetailHandler handler) const
+bool MachOAnalyzer::forEachRebase_Relocations(Diagnostics& diag, const LinkEditInfo& leInfo, const SegmentInfo segmentsInfo[], RebaseDetailHandler handler) const
 {
     // old binary, walk relocations
     const uint64_t                  relocsStartAddress = localRelocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
@@ -5452,28 +5632,27 @@
     __block uint64_t linkeditFileOffset = 0;
     __block const uint8_t* linkeditStartAddr = nullptr;
 
-    const Header* hdr       = ((const Header*)this);
-    uint32_t numSegments    = hdr->segmentCount();
+    uint32_t numSegments = this->segmentCount();
     BLOCK_ACCCESSIBLE_ARRAY(mach_o::SegmentLayout, segmentLayout, numSegments);
-    hdr->forEachSegment(^(const Header::SegmentInfo &info, bool &stop) {
+    this->forEachSegment(^(const SegmentInfo &info, bool &stop) {
         mach_o::SegmentLayout segment;
-        segment.vmAddr      = info.vmaddr;
-        segment.vmSize      = info.vmsize;
+        segment.vmAddr      = info.vmAddr;
+        segment.vmSize      = info.vmSize;
         segment.fileOffset  = info.fileOffset;
         segment.fileSize    = info.fileSize;
-        segment.buffer      = (uint8_t*)(info.vmaddr + slide);
-        segment.protections = info.initProt;
+        segment.buffer      = (uint8_t*)(info.vmAddr + slide);
+        segment.protections = info.protections;
 
         segment.kind        = mach_o::SegmentLayout::Kind::unknown;
-        if ( info.segmentName == "__TEXT" ) {
+        if ( !strcmp(info.segName, "__TEXT") ) {
             segment.kind    = mach_o::SegmentLayout::Kind::text;
-        } else if ( info.segmentName == "__LINKEDIT" ) {
+        } else if ( !strcmp(info.segName, "__LINKEDIT") ) {
             segment.kind    = mach_o::SegmentLayout::Kind::linkedit;
             linkeditFileOffset = info.fileOffset;
             linkeditStartAddr = segment.buffer;
         }
 
-        segmentLayout[info.segmentIndex] = segment;
+        segmentLayout[info.segIndex] = segment;
     });
 
     mach_o::LinkeditLayout linkedit;