Loading...
mach_o/Header.cpp dyld-1340 dyld-1162
--- dyld/dyld-1340/mach_o/Header.cpp
+++ dyld/dyld-1162/mach_o/Header.cpp
@@ -33,93 +33,35 @@
   #include <unistd.h>
 #endif
 
-#include <mach-o/fixup-chains.h>
-
 #include "Array.h"
 #include "Header.h"
 #include "Architecture.h"
 #include "Misc.h"
 #include "Policy.h"
 #include "LoggingStub.h"
-#include "TargetPolicy.h"
-#include "Version32.h"
-
-#if TARGET_OS_EXCLAVEKIT
-#ifndef VM_PROT_READ
-    #define VM_PROT_READ    1
-#endif
-
-#ifndef VM_PROT_WRITE
-    #define VM_PROT_WRITE   2
-#endif
-
-#ifndef VM_PROT_EXECUTE
-    #define VM_PROT_EXECUTE 4
-#endif
-#endif // TARGET_OS_EXCLAVEKIT
 
 using mach_o::Architecture;
 using mach_o::Platform;
 using mach_o::PlatformAndVersions;
 using mach_o::Policy;
-using mach_o::Version32;
 
 namespace mach_o {
 
 
-
 //
-// MARK: --- LinkedDylibAttributes ---
+// MARK: --- DependentDylibAttributes ---
 //
 
-constinit const LinkedDylibAttributes LinkedDylibAttributes::regular;
-constinit const LinkedDylibAttributes LinkedDylibAttributes::justWeakLink(DYLIB_USE_WEAK_LINK);
-constinit const LinkedDylibAttributes LinkedDylibAttributes::justUpward(DYLIB_USE_UPWARD);
-constinit const LinkedDylibAttributes LinkedDylibAttributes::justReExport(DYLIB_USE_REEXPORT);
-constinit const LinkedDylibAttributes LinkedDylibAttributes::justDelayInit(DYLIB_USE_DELAYED_INIT);
-
-void LinkedDylibAttributes::toString(char buf[64]) const
-{
-    buf[0] = '\0';
-    if ( weakLink )
-        strlcpy(buf, "weak-link", 64);
-    if ( upward ) {
-        if ( buf[0] != '\0' )
-            strlcat(buf, ",", 64);
-        strlcat(buf, "upward", 64);
-    }
-    if ( delayInit ) {
-        if ( buf[0] != '\0' )
-            strlcat(buf, ",", 64);
-        strlcat(buf, "delay-init", 64);
-    }
-    if ( reExport ) {
-        if ( buf[0] != '\0' )
-            strlcat(buf, ",", 64);
-        strlcat(buf, "re-export", 64);
-    }
-}
+constinit const DependentDylibAttributes DependentDylibAttributes::regular;
+constinit const DependentDylibAttributes DependentDylibAttributes::justWeakLink(DYLIB_USE_WEAK_LINK);
+constinit const DependentDylibAttributes DependentDylibAttributes::justUpward(DYLIB_USE_UPWARD);
+constinit const DependentDylibAttributes DependentDylibAttributes::justReExport(DYLIB_USE_REEXPORT);
+constinit const DependentDylibAttributes DependentDylibAttributes::justDelayInit(DYLIB_USE_DELAYED_INIT);
 
 
 //
 // MARK: --- methods that read mach_header ---
 //
-
-bool Header::isSharedCacheEligiblePath(const char* dylibName) {
-    return (   (strncmp(dylibName, "/usr/lib/", 9) == 0)
-            || (strncmp(dylibName, "/System/Library/", 16) == 0)
-            || (strncmp(dylibName, "/System/iOSSupport/usr/lib/", 27) == 0)
-            || (strncmp(dylibName, "/System/iOSSupport/System/Library/", 34) == 0)
-            || (strncmp(dylibName, "/Library/Apple/usr/lib/", 23) == 0)
-            || (strncmp(dylibName, "/Library/Apple/System/Library/", 30) == 0)
-            || (strncmp(dylibName, "/System/DriverKit/", 18) == 0)
-            || (strncmp(dylibName, "/System/Cryptexes/OS/usr/lib/", 29) == 0)
-            || (strncmp(dylibName, "/System/Cryptexes/OS/System/Library/", 36) == 0)
-            || (strncmp(dylibName, "/System/Cryptexes/OS/System/iOSSupport/usr/lib/", 47) == 0)
-            || (strncmp(dylibName, "/System/Cryptexes/OS/System/iOSSupport/System/Library/", 54) == 0)
-            || (strncmp(dylibName, "/System/ExclaveKit/usr/lib/", 27) == 0)
-            || (strncmp(dylibName, "/System/ExclaveKit/System/Library/", 34) == 0));
-}
 
 bool Header::hasMachOMagic() const
 {
@@ -147,11 +89,6 @@
         return 8;
     else
         return 4;
-}
-
-uint32_t Header::ncmds() const
-{
-    return mh.ncmds;
 }
 
 bool Header::uses16KPages() const
@@ -191,10 +128,9 @@
 bool Header::isDyldManaged() const
 {
     switch ( mh.filetype ) {
+        case MH_BUNDLE:
+        case MH_EXECUTE:
         case MH_DYLIB:
-        case MH_BUNDLE:
-            return true;
-        case MH_EXECUTE:
             return ((mh.flags & MH_DYLDLINK) != 0);
         default:
             break;
@@ -207,16 +143,6 @@
     return (mh.filetype == MH_DYLIB);
 }
 
-bool Header::isDylibOrStub() const
-{
-    return (mh.filetype == MH_DYLIB) || (mh.filetype == MH_DYLIB_STUB);
-}
-
-bool Header::isDylibStub() const
-{
-    return (mh.filetype == MH_DYLIB_STUB);
-}
-
 bool Header::isBundle() const
 {
     return (mh.filetype == MH_BUNDLE);
@@ -261,11 +187,6 @@
     return (mh.filetype == MH_PRELOAD);
 }
 
-bool Header::usesTwoLevelNamespace() const
-{
-    return (mh.flags & MH_TWOLEVEL);
-}
-
 bool Header::hasWeakDefs() const
 {
     return (mh.flags & MH_WEAK_DEFINES);
@@ -286,10 +207,9 @@
     if ( content.size() < sizeof(mach_header) )
         return nullptr;
 
-    if ( const Header* mh = (const Header*)content.data() ) {
-        if ( mh->hasMachOMagic() )
-            return mh;
-    }
+    const Header* mh = (const Header*)content.data();
+    if ( mh->hasMachOMagic() )
+        return mh;
     return nullptr;
 }
 
@@ -324,11 +244,6 @@
     return (this->mh.flags & MH_SIM_SUPPORT) != 0;
 }
 
-bool Header::noDynamicAccess() const
-{
-    return (this->mh.flags & MH_NO_DYNAMIC_ACCESS) != 0;
-}
-
 
 //
 // MARK: --- methods for validating mach-o content ---
@@ -340,17 +255,13 @@
     __block PlatformAndVersions pvs;
     forEachPlatformLoadCommand(^(Platform platform, Version32 minOS, Version32 sdk) {
         Error err = pvs.zip({ platform, minOS, sdk });
-        // .zip() will not combine platforms if it is an invalid combo
+        assert(err.noError());
     });
     return pvs;
 }
 
 Error Header::validSemanticsPlatform() const
 {
-    // Kernel Collections (MH_FILESET) don't have a platform. Skip them
-    if ( isFileSet() )
-        return Error::none();
-
     // should be one platform load command (exception is zippered dylibs)
     __block PlatformAndVersions pvs;
     __block Error           badPlatform;
@@ -366,16 +277,10 @@
     if ( badPlatform )
         return std::move(badPlatform);
 
-    if ( pvs.platform.empty() && gHeaderAllowEmptyPlatform )
+#if BUILDING_MACHO_WRITER
+    if ( pvs.platform.empty() )
         return Error::none(); // allow empty platform in static linker
-
-    // preloads usually don't have a platform
-    if ( isPreload() )
-        return Error::none();
-
-    // static executables also may not have one
-    if ( isStaticExecutable() )
-        return Error::none();
+#endif
 
     return pvs.platform.valid();
 }
@@ -383,7 +288,7 @@
 Error Header::valid(uint64_t fileSize) const
 {
     if ( fileSize < sizeof(mach_header) )
-        return Error("file is too small (length=%llu)", fileSize);
+        return Error("file is too short");
 
     if ( !hasMachOMagic() )
         return Error("not a mach-o file (start is no MH_MAGIC[_64])");
@@ -403,7 +308,7 @@
     if ( Error err = validSemanticsInstallName(policy) )
         return err;
 
-    if ( Error err = validSemanticsLinkedDylibs(policy) )
+    if ( Error err = validSemanticsDependents(policy) )
         return err;
 
     if ( Error err = validSemanticsRPath(policy) )
@@ -430,7 +335,7 @@
 
     const char* str = (char*)cmd + strOffset;
     const char* end = (char*)cmd + cmd->cmdsize;
-    for ( const char* s = end-1; s >= str; --s ) {
+    for ( const char* s = str; s < end; ++s ) {
         if ( *s == '\0' ) {
             return Error::none();
         }
@@ -446,11 +351,10 @@
         return Error("load commands length (%llu) exceeds length of file (%llu)", headerAndLCSize, fileSize);
     }
 
-    // check for recognized filetype
+    // check for reconized filetype
     switch ( mh.filetype ) {
         case MH_EXECUTE:
         case MH_DYLIB:
-        case MH_DYLIB_STUB:
         case MH_DYLINKER:
         case MH_BUNDLE:
         case MH_KEXT_BUNDLE:
@@ -574,14 +478,6 @@
             case LC_FILESET_ENTRY:
                 fileSetCmd = (fileset_entry_command*)cmd;
                 lcError    = stringOverflow(cmd, index, fileSetCmd->entry_id.offset);
-                break;
-            case LC_FUNCTION_VARIANTS:
-                if ( cmd->cmdsize != sizeof(linkedit_data_command) )
-                    lcError = Error("load command #%d LC_FUNCTION_VARIANTS size wrong", index);
-                break;
-            case LC_FUNCTION_VARIANT_FIXUPS:
-                if ( cmd->cmdsize != sizeof(linkedit_data_command) )
-                    lcError = Error("load command #%d LC_FUNCTION_VARIANT_FIXUPS size wrong", index);
                 break;
             default:
                 if ( cmd->cmd & LC_REQ_DYLD )
@@ -634,12 +530,11 @@
     });
     if ( uuidCount > 1 )
         return Error("too many LC_UUID load commands");
-    if ( (uuidCount == 0) && policy.enforceHasUUID() ) {
-            return Error("missing LC_UUID load command");
-    }
+    if ( (uuidCount == 0) && policy.enforceHasUUID() )
+        return Error("missing LC_UUID load command");
+
     return Error::none();
 }
-
 
 Error Header::validSemanticsInstallName(const Policy& policy) const
 {
@@ -655,7 +550,7 @@
     if ( foundCount > 1 )
         return Error("multiple LC_ID_DYLIB found");
 
-    if ( this->isDylibOrStub() ) {
+    if ( this->isDylib() ) {
         if ( installName == nullptr )
             return Error("MH_DYLIB is missing LC_ID_DYLIB");
 #if 0 // FIXME: need path plumbed down
@@ -683,7 +578,7 @@
     return Error::none();
 }
 
-Error Header::validSemanticsLinkedDylibs(const Policy& policy) const
+Error Header::validSemanticsDependents(const Policy& policy) const
 {
     // gather info
     __block Error dupDepError;
@@ -692,7 +587,7 @@
     const char**  depPaths = depPathsBuffer;
     const bool    enforceNoDupDylibs = policy.enforceNoDuplicateDylibs();
     const bool    hasWarningHandler = mach_o::hasWarningHandler();
-    // don't use forEachLinkedDylib, because it synthesizes libSystem.dylib
+    // don't use forEachDependentDylib, because it synthesizes libSystem.dylib
     forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
         switch ( cmd->cmd ) {
             case LC_LOAD_DYLIB:
@@ -705,10 +600,10 @@
                     for ( int i = 0; i < depCount; ++i ) {
                         if ( strcmp(loadPath, depPaths[i]) == 0 ) {
                             if ( enforceNoDupDylibs ) {
-                                dupDepError = Error("duplicate linked dylib '%s'", loadPath);
+                                dupDepError = Error("duplicate dependent dylib '%s'", loadPath);
                                 stop        = true;
                             } else
-                                warning(this, "duplicate linked dylib are deprecated ('%s')", loadPath);
+                                warning(this, "duplicate dependent dylib are deprecated ('%s')", loadPath);
                         }
                     }
                     depPaths[depCount] = loadPath;
@@ -723,9 +618,24 @@
     // all new binaries must link with something
     if ( this->isDyldManaged() && policy.enforceHasLinkedDylibs() && (depCount == 0) ) {
         // except for dylibs in libSystem.dylib which are ok to link with nothing (they are on bottom)
-        CString installName = this->installName();
-        CString libSystemDir = platformAndVersions().platform.libSystemDir();
-        if ( !installName.starts_with(libSystemDir) )
+        const char* installName = this->installName();
+        bool isLibSystem = false;
+        if (installName != nullptr) {
+            if ( this->builtForPlatform(Platform::driverKit, true) ) {
+                const char* libSystemDir = "/System/DriverKit/usr/lib/system/";
+                if ( strncmp(installName, libSystemDir, strlen(libSystemDir)) != 0 )
+                    isLibSystem = true;
+            } else if ( this->platformAndVersions().platform.isExclaveKit() ) {
+                const char* libSystemDir = "/System/ExclaveKit/usr/lib/system/";
+                if ( strncmp(installName, libSystemDir, strlen(libSystemDir)) != 0 )
+                    isLibSystem = true;
+            } else {
+                const char* libSystemDir = "/usr/lib/system/";
+                if ( strncmp(installName, libSystemDir, strlen(libSystemDir)) != 0 )
+                    isLibSystem = true;
+            }
+        }
+        if ( !isLibSystem )
             return Error("missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)");
     }
     return Error::none();
@@ -769,12 +679,8 @@
         return Error("segment '%s' load command content extends beyond end of file", seg->segname);
 
     // <rdar://problem/19986776> dyld should support non-allocatable __LLVM segment
-    // <rdar://problem/144216621> dyld should support non-allocatable __DWARF segment (golang)
-    if ( !isObjectFile() && (seg->filesize > seg->vmsize) ) {
-        // non-mapped segments must have vmsize==0 and initprot==0
-        if ( (seg->vmsize == 0) && (seg->initprot == 0) )
-            return Error::none(); // no more checks of segment because it is not mapped
-        else
+    if ( !isObjectFile() ) {
+        if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) )
             return Error("segment '%s' filesize exceeds vmsize", seg->segname);
     }
 
@@ -782,7 +688,7 @@
     if ( (seg->initprot & 0xFFFFFFF8) != 0 ) {
         return Error("%s segment permissions has invalid bits set (0x%08X)", seg->segname, seg->initprot);
     }
-    if ( this->isDyldManaged() && policy.enforceTextSegmentPermissions() ) {
+    if ( policy.enforceTextSegmentPermissions() ) {
         if ( (strcmp(seg->segname, "__TEXT") == 0) && (seg->initprot != (VM_PROT_READ | VM_PROT_EXECUTE)) )
             return Error("__TEXT segment permissions is not 'r-x'");
     }
@@ -790,26 +696,17 @@
         if ( (strcmp(seg->segname, "__LINKEDIT") == 0) && (seg->initprot != VM_PROT_READ) )
             return Error("__LINKEDIT segment permissions is not 'r--'");
     }
-    // dylib stubs don't have LC_SEGMENT_SPLIT_INFO, so they trip on SG_READ_ONLY check because
-    // the split seg info exception can't be applied
-    if ( !isDylibStub() && policy.enforceDataSegmentPermissions() ) {
+    if ( policy.enforceDataSegmentPermissions() ) {
         if ( (strcmp(seg->segname, "__DATA") == 0) && (seg->initprot != (VM_PROT_READ | VM_PROT_WRITE)) )
             return Error("__DATA segment permissions is not 'rw-'");
         if ( strcmp(seg->segname, "__DATA_CONST") == 0 ) {
             if ( seg->initprot != (VM_PROT_READ | VM_PROT_WRITE) )
                 return Error("__DATA_CONST segment permissions is not 'rw-'");
             if ( (seg->flags & SG_READ_ONLY) == 0 ) {
-                bool isSplitSegMarker = false;
-                if ( this->isDylibOrStub() && this->hasSplitSegInfo(isSplitSegMarker) && !isSplitSegMarker ) {
-                    // dylibs intended for dyld cache are allowed to not have SG_READ_ONLY set
-                }
-                else if ( this->inDyldCache() ) {
+                if ( this->isDylib() && this->hasSplitSegInfo() ) {
                     // dylibs in dyld cache are allowed to not have SG_READ_ONLY set
                 }
-                else if ( this->isStaticExecutable() ) {
-                    // static excutables don't use dyld so have no way to make DATA_CONST read-only
-                }
-                else if ( policy.enforceDataConstSegmentPermissions() ) {
+                else {
                     return Error("__DATA_CONST segment missing SG_READ_ONLY flag");
                 }
             }
@@ -820,32 +717,28 @@
     if ( (seg->vmaddr + seg->vmsize) < seg->vmaddr )
         return Error("'%s' segment vm range wraps", seg->segname);
 
-    // dylib stubs have no section data
-    if ( !isDylibStub() ) {
-        // check sections are within its segment
-        const SC* const sectionsStart = (SC*)((char*)seg + sizeof(SG));
-        const SC* const sectionsEnd   = &sectionsStart[seg->nsects];
-        for ( const SC* sect = sectionsStart; (sect < sectionsEnd); ++sect ) {
-            if ( (int64_t)(sect->size) < 0 ) {
-                return Error("section '%s' size too large 0x%lX", sect->sectname, (size_t)sect->size);
-            }
-            else if ( sect->addr < seg->vmaddr ) {
-                return Error("section '%s' start address 0x%lX is before containing segment's address 0x%0lX", sect->sectname, (size_t)sect->addr, (size_t)seg->vmaddr);
-            }
-            else if ( policy.enforceSectionsInSegment() && (sect->addr + sect->size > seg->vmaddr + seg->vmsize) ) {
-                return Error("section '%s' end address 0x%lX is beyond containing segment's end address 0x%0lX", sect->sectname, (size_t)(sect->addr + sect->size), (size_t)(seg->vmaddr + seg->vmsize));
-            }
+    // check sections are within its segment
+    const SC* const sectionsStart = (SC*)((char*)seg + sizeof(SG));
+    const SC* const sectionsEnd   = &sectionsStart[seg->nsects];
+    for ( const SC* sect = sectionsStart; (sect < sectionsEnd); ++sect ) {
+        if ( (int64_t)(sect->size) < 0 ) {
+            return Error("section '%s' size too large 0x%lX", sect->sectname, (size_t)sect->size);
+        }
+        else if ( sect->addr < seg->vmaddr ) {
+            return Error("section '%s' start address 0x%lX is before containing segment's address 0x%0lX", sect->sectname, (size_t)sect->addr, (size_t)seg->vmaddr);
+        }
+        else if ( policy.enforceSectionsInSegment() && (sect->addr + sect->size > seg->vmaddr + seg->vmsize) ) {
+            return Error("section '%s' end address 0x%lX is beyond containing segment's end address 0x%0lX", sect->sectname, (size_t)(sect->addr + sect->size), (size_t)(seg->vmaddr + seg->vmsize));
         }
     }
 
     return Error::none();
 }
+
 #endif // !TARGET_OS_EXCLAVEKIT
-
-struct VIS_HIDDEN Interval
+struct Interval
 {
     bool     overlaps(const Interval& other) const;
-    uint64_t size() const { return end - start; }
     uint64_t start;
     uint64_t end;
 };
@@ -862,13 +755,14 @@
     {
         Interval    vm;
         Interval    file;
-        CString     name;
+        const char* name;
     };
     STACK_ALLOC_OVERFLOW_SAFE_ARRAY(SegRange, ranges, 12);
     __block Error     lcError;
     __block bool      hasTEXT              = false;
     __block bool      hasLINKEDIT          = false;
     __block uint64_t segmentIndexText     = 0;
+    __block uint64_t segmentIndexLinkedit = 0;
     forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
         if ( cmd->cmd == LC_SEGMENT_64 ) {
             const segment_command_64* seg64 = (segment_command_64*)cmd;
@@ -878,6 +772,7 @@
             }
             else if ( strcmp(seg64->segname, "__LINKEDIT") == 0 ) {
                 hasLINKEDIT          = true;
+                segmentIndexLinkedit = ranges.count();
             }
             lcError = validSegment<segment_command_64, section_64>(policy, fileSize, seg64);
             ranges.push_back({ { seg64->vmaddr, seg64->vmaddr + seg64->vmsize }, { seg64->fileoff, seg64->fileoff + seg64->filesize }, seg64->segname });
@@ -890,6 +785,7 @@
             }
             else if ( strcmp(seg32->segname, "__LINKEDIT") == 0 ) {
                 hasLINKEDIT          = true;
+                segmentIndexLinkedit = ranges.count();
             }
             lcError = validSegment<segment_command, section>(policy, fileSize, seg32);
             ranges.push_back({ { seg32->vmaddr, seg32->vmaddr + seg32->vmsize }, { seg32->fileoff, seg32->fileoff + seg32->filesize }, seg32->segname });
@@ -903,56 +799,50 @@
     // dynamic binaries have further restrictions
     if ( isDyldManaged() ) {
         if ( hasTEXT ) {
-            if ( !this->inDyldCache() && (ranges[segmentIndexText].file.start != 0) )
+            if ( ranges[segmentIndexText].file.start != 0 )
                 return Error("__TEXT segment fileoffset is not zero");
             const uint32_t headerAndLCSize = machHeaderSize() + mh.sizeofcmds;
-            if ( ranges[segmentIndexText].file.size() < headerAndLCSize )
-                return Error("load commands do not fit in __TEXT segment filesize");
-            if ( ranges[segmentIndexText].vm.size() < headerAndLCSize )
-                return Error("load commands do not fit in __TEXT segment vmsize");
+            if ( ranges[segmentIndexText].file.end < headerAndLCSize )
+                return Error("load commands do not fit in __TEXT segment");
         }
         else {
             return Error("missing __TEXT segment");
         }
-        (void)hasLINKEDIT;
         // FIXME: LINKEDIT checks need to move to Analyzer
         //if ( !hasLINKEDIT )
         //    return Error("missing __LINKEDIT segment");
     }
 
     // check for overlapping segments, by looking at every possible pair of segments
-    const bool checkNames = isDyldManaged() && policy.enforceUniqueSegmentNames();
     for ( const SegRange& r1 : ranges ) {
         for ( const SegRange& r2 : ranges ) {
             if ( &r1 == &r2 )
                 continue;
             if ( r1.vm.overlaps(r2.vm) )
-                return Error("vm range of segment '%s' overlaps segment '%s'", r1.name.c_str(), r2.name.c_str());
-            // can't compare file offsets for segments in dyld cache because they may be offsets into different files
-            if ( !this->inDyldCache() && r1.file.overlaps(r2.file) )
-                return Error("file range of segment '%s' overlaps segment '%s'", r1.name.c_str(), r2.name.c_str());
-            if ( checkNames && (r1.name == r2.name) )
-                return Error("duplicate segment name '%s'", r1.name.c_str());
+                return Error("vm range of segment '%s' overlaps segment '%s'", r1.name, r2.name);
+            if ( r1.file.overlaps(r2.file) )
+                return Error("file range of segment '%s' overlaps segment '%s'", r1.name, r2.name);
         }
     }
 
     // check segment load command order matches file content order which matches vm order
     // skip dyld cache because segments are moved around too much
-    if ( policy.enforceSegmentOrderMatchesLoadCmds() && !inDyldCache() && (ranges.count() > 1) ) {
-        for (int i=1; i < ranges.count()-1; ++i) {
-            const SegRange& a = ranges[i-1];
-            const SegRange& b = ranges[i];
-            if ( (b.file.start < a.file.start) && (b.file.start != b.file.end) )
-                return Error("segment '%s' file offset out of order", a.name.c_str());
-            if ( b.vm.start < a.vm.start ) {
-                if ( isFileSet() && (b.name == "__PRELINK_INFO") ) {
-                    // __PRELINK_INFO may have no vmaddr set
-                } else if ( arch().usesx86_64Instructions() && isDynamicExecutable() && !isPIE() ) {
-                    // rdar://149897776 (allow out of VM address order segments on legacy x86 binaries)
-                } else {
-                    return Error("segment '%s' vm address out of order", b.name.c_str());
+    if ( policy.enforceSegmentOrderMatchesLoadCmds() && !inDyldCache() ) {
+        const SegRange* last = nullptr;
+        for ( const SegRange& r : ranges ) {
+            if ( last != nullptr ) {
+                if ( (r.file.start < last->file.start) && (r.file.start != r.file.end) )
+                    return Error("segment '%s' file offset out of order", r.name);
+                if ( r.vm.start < last->vm.start ) {
+                    if ( isFileSet() && (strcmp(r.name, "__PRELINK_INFO") == 0) ) {
+                        // __PRELINK_INFO may have no vmaddr set
+                    }
+                    else {
+                        return Error("segment '%s' vm address out of order", r.name);
+                    }
                 }
             }
+            last = &r;
         }
     }
 
@@ -1098,14 +988,6 @@
     return !hasLoadCommand(LC_LOAD_DYLINKER);
 }
 
-bool Header::isDylinker() const {
-    if ( mh.filetype != MH_DYLINKER ) {
-        return false;
-    }
-    return true;
-}
-
-
 //
 // MARK: --- methods that read Platform load commands ---
 //
@@ -1153,26 +1035,24 @@
                 break;
         }
     });
-
-    if ( foundPlatform )
+#ifdef BUILDING_MACHO_WRITER
+    // no implicit platforms in static linker
+    // but for object files only, we need to support linking against old macos dylibs
+    if ( isObjectFile() )
         return;
-
-    if ( !gHeaderAddImplicitPlatform ) {
-        // no implicit platforms in static linker
-        // but for object files and -preload files only, we need to support linking against old macos dylibs
-        if ( isObjectFile() || isPreload() || isKextBundle() || isStaticExecutable() )
-            return;
-    }
-
-    // old binary with no explicit platform
+#endif
+
+    if ( !foundPlatform ) {
+        // old binary with no explicit platform
 #if TARGET_OS_OSX
-    if ( (mh.cputype == CPU_TYPE_X86_64) || (mh.cputype == CPU_TYPE_I386) )
-        handler(Platform::macOS, Version32(10, 5), Version32(10, 5)); // guess it is a macOS 10.5 binary
-    // <rdar://problem/75343399>
-    // The Go linker emits non-standard binaries without a platform and we have to live with it.
-    if ( mh.cputype == CPU_TYPE_ARM64 )
-        handler(Platform::macOS, Version32(11, 0), Version32(11, 0)); // guess it is a macOS 11.0 binary
+        if ( (mh.cputype == CPU_TYPE_X86_64) | (mh.cputype == CPU_TYPE_I386) )
+            handler(Platform::macOS, Version32(10, 5), Version32(10, 5)); // guess it is a macOS 10.5 binary
+        // <rdar://problem/75343399>
+        // The Go linker emits non-standard binaries without a platform and we have to live with it.
+        if ( mh.cputype == CPU_TYPE_ARM64 )
+            handler(Platform::macOS, Version32(11, 0), Version32(11, 0)); // guess it is a macOS 11.0 binary
 #endif
+    }
 }
 
 bool Header::builtForPlatform(Platform reqPlatform, bool onlyOnePlatform) const
@@ -1193,29 +1073,10 @@
     return match;
 }
 
-bool Header::builtForSimulator() const {
-    return platformAndVersions().platform.isSimulator();
-}
-
-void Header::forEachBuildTool(void (^handler)(Platform platform, uint32_t tool, uint32_t version)) const
-{
-    forEachLoadCommandSafe(^(const load_command* cmd, bool &stop) {
-        switch ( cmd->cmd ) {
-            case LC_BUILD_VERSION: {
-                const build_version_command* buildCmd = (build_version_command *)cmd;
-                for ( uint32_t i = 0; i != buildCmd->ntools; ++i ) {
-                    uint32_t offset = sizeof(build_version_command) + (i * sizeof(build_tool_version));
-                    if ( offset >= cmd->cmdsize )
-                        break;
-
-                    const build_tool_version* firstTool = (const build_tool_version*)(&buildCmd[1]);
-                    handler(Platform(buildCmd->platform), firstTool[i].tool, firstTool[i].version);
-                }
-            }
-        }
-    });
-}
-
+bool Header::isZippered() const
+{
+    return platformAndVersions().platform == Platform::zippered;
+}
 
 bool Header::allowsAlternatePlatform() const
 {
@@ -1243,7 +1104,7 @@
 {
     __block bool found = false;
     forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_ID_DYLIB || cmd->cmd == LC_ID_DYLINKER) {
+        if ( cmd->cmd == LC_ID_DYLIB ) {
             const dylib_command* dylibCmd = (dylib_command*)cmd;
             *compatVersion                = Version32(dylibCmd->dylib.compatibility_version);
             *currentVersion               = Version32(dylibCmd->dylib.current_version);
@@ -1271,23 +1132,8 @@
     return found;
 }
 
-bool Header::sourceVersion(Version64 &version) const
-{
-    __block bool found = false;
-    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_SOURCE_VERSION ) {
-            const source_version_command* svc = (const source_version_command*)cmd;
-            version = Version64(svc->version);
-            found = true;
-            stop  = true;
-        }
-    });
-    
-    return found;
-}
-
-
-const char* Header::linkedDylibLoadPath(uint32_t depIndex) const
+
+const char* Header::dependentDylibLoadPath(uint32_t depIndex) const
 {
     __block uint32_t     curIndex  = 0;
     __block const char*  result    = nullptr;
@@ -1307,14 +1153,14 @@
     return result;
 }
 
-uint32_t Header::linkedDylibCount(bool* allDepsAreNormal) const
+uint32_t Header::dependentDylibCount(bool* allDepsAreNormal) const
 {
     if ( allDepsAreNormal != nullptr )
         *allDepsAreNormal = true;
     __block unsigned count   = 0;
-    this->forEachLinkedDylib(^(const char* loadPath, LinkedDylibAttributes kind, Version32 , Version32 , bool , bool& stop) {
+    this->forEachDependentDylib(^(const char* loadPath, DependentDylibAttributes kind, Version32 , Version32 , bool& stop) {
         if ( allDepsAreNormal != nullptr ) {
-            if ( kind != LinkedDylibAttributes::regular )
+            if ( kind != DependentDylibAttributes::regular )
                 *allDepsAreNormal = false;  // record if any linkages were weak, re-export, upward, or delay-init
         }
         ++count;
@@ -1322,9 +1168,9 @@
     return count;
 }
 
-LinkedDylibAttributes Header::loadCommandToDylibKind(const dylib_command* dylibCmd)
-{
-    LinkedDylibAttributes attr;
+DependentDylibAttributes Header::loadCommandToDylibKind(const dylib_command* dylibCmd)
+{
+    DependentDylibAttributes attr;
     const dylib_use_command* dylib2Cmd = (dylib_use_command*)dylibCmd;
     if ( (dylib2Cmd->marker == 0x1a741800) && (dylib2Cmd->nameoff == sizeof(dylib_use_command)) ) {
         attr.raw = (uint8_t)dylib2Cmd->flags;
@@ -1346,13 +1192,9 @@
     assert(0 && "not a dylib load command");
 }
 
-void Header::forEachLinkedDylib(void (^callback)(const char* loadPath, LinkedDylibAttributes kind,
-                                                 Version32 compatVersion, Version32 curVersion,
-                                                 bool synthesizedLink, bool& stop)) const
-{
-    if ( this->isDylinker() )
-        return;
-
+void Header::forEachDependentDylib(void (^callback)(const char* loadPath, DependentDylibAttributes kind,
+                                                    Version32 compatVersion, Version32 curVersion, bool& stop)) const
+{
     __block unsigned count   = 0;
     __block bool     stopped = false;
     forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
@@ -1363,7 +1205,7 @@
             case LC_LOAD_UPWARD_DYLIB: {
                 const dylib_command* dylibCmd = (dylib_command*)cmd;
                 const char*          loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset;
-                callback(loadPath, loadCommandToDylibKind(dylibCmd), Version32(dylibCmd->dylib.compatibility_version), Version32(dylibCmd->dylib.current_version), false, stop);
+                callback(loadPath, loadCommandToDylibKind(dylibCmd), Version32(dylibCmd->dylib.compatibility_version), Version32(dylibCmd->dylib.current_version), stop);
                 ++count;
                 if ( stop )
                     stopped = true;
@@ -1371,23 +1213,24 @@
             }
         }
     });
-
+#if BUILDING_DYLD
     // everything must link with something
     if ( (count == 0) && !stopped ) {
         // The dylibs that make up libSystem can link with nothing
         // except for dylibs in libSystem.dylib which are ok to link with nothing (they are on bottom)
         if ( this->builtForPlatform(Platform::driverKit, true) ) {
             if ( !this->isDylib() || (strncmp(this->installName(), "/System/DriverKit/usr/lib/system/", 33) != 0) )
-                callback("/System/DriverKit/usr/lib/libSystem.B.dylib", LinkedDylibAttributes::regular, Version32(1, 0), Version32(1, 0), true, stopped);
+                callback("/System/DriverKit/usr/lib/libSystem.B.dylib", DependentDylibAttributes::regular, Version32(1, 0), Version32(1, 0), stopped);
         } else if ( this->platformAndVersions().platform.isExclaveKit() ) {
             if ( !this->isDylib() || (strncmp(this->installName(), "/System/ExclaveKit/usr/lib/system/", 34) != 0) )
-                callback("/System/ExclaveKit/usr/lib/libSystem.dylib", LinkedDylibAttributes::regular, Version32(1, 0), Version32(1, 0), true, stopped);
+                callback("/System/ExclaveKit/usr/lib/libSystem.dylib", DependentDylibAttributes::regular, Version32(1, 0), Version32(1, 0), stopped);
         }
         else {
             if ( !this->isDylib() || (strncmp(this->installName(), "/usr/lib/system/", 16) != 0) )
-                callback("/usr/lib/libSystem.B.dylib", LinkedDylibAttributes::regular, Version32(1, 0), Version32(1, 0), true, stopped);
-        }
-    }
+                callback("/usr/lib/libSystem.B.dylib", DependentDylibAttributes::regular, Version32(1, 0), Version32(1, 0), stopped);
+        }
+    }
+#endif
 }
 
 void Header::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
@@ -1399,8 +1242,10 @@
             // only process variables that start with DYLD_ and end in _PATH
             if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
                 const char* equals = strchr(keyEqualsValue, '=');
-                if ( equals != nullptr ) {
-                    callback(keyEqualsValue, stop);
+                if ( equals != NULL ) {
+                    if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
+                        callback(keyEqualsValue, stop);
+                    }
                 }
             }
         }
@@ -1432,9 +1277,8 @@
             }
             break;
         case CPU_TYPE_ARM64:
-        case CPU_TYPE_ARM64_32:
             if ( flavor == 6 ) {   // ARM_THREAD_STATE64
-                memcpy((uint32_t*)&addr, (uint32_t*)&regs64[32], sizeof(uint64_t)); // arm_thread_state64_t.__pc
+                addr = regs64[32]; // arm_thread_state64_t.__pc
                 return true;
             }
             break;
@@ -1479,29 +1323,7 @@
             stop                          = true;
         }
     });
-
-    // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
-    if ( mh.cputype == CPU_TYPE_X86_64 ) {
-        PlatformAndVersions pvs = platformAndVersions();
-        if ( (pvs.platform == Platform::macOS) && (pvs.sdk < Version32(10,9)) )
-            return false;
-    }
-
-    return result;
-}
-
-bool Header::hasLinkerOptimizationHints(uint32_t& fileOffset, uint32_t& size) const
-{
-    __block bool result = false;
-    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_LINKER_OPTIMIZATION_HINT ) {
-            linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
-            fileOffset                    = sigCmd->dataoff;
-            size                          = sigCmd->datasize;
-            result                        = true;
-            stop                          = true;
-        }
-    });
+    // FIXME: may need to ignore codesigs from pre 10.9 macOS binaries
     return result;
 }
 
@@ -1520,30 +1342,11 @@
     return result;
 }
 
-bool Header::hasClassicRelocations(uint32_t& nLocRel, uint32_t& nExtRel) const
+bool Header::hasSplitSegInfo() const
 {
     __block bool result = false;
-    nLocRel = 0;
-    nExtRel = 0;
-    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_DYSYMTAB) {
-            dysymtab_command* dySymCmd = (dysymtab_command*)cmd;
-            nLocRel = dySymCmd->nlocrel;
-            nExtRel = dySymCmd->nextrel;
-            result      = (nLocRel != 0 || nExtRel != 0);
-            stop        = true;
-        }
-    });
-    return result;
-}
-
-bool Header::hasSplitSegInfo(bool& isSplitSegMarker) const
-{
-    __block bool result = false;
     forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
         if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO) {
-            linkedit_data_command* sigCmd = (linkedit_data_command*)cmd;
-            isSplitSegMarker = (sigCmd->datasize == 0);
             result      = true;
             stop        = true;
         }
@@ -1566,741 +1369,6 @@
     return result;
 }
 
-bool Header::hasFunctionsVariantTable(uint64_t& runtimeOffset) const
-{
-    __block uint32_t fileOffset = 0;
-    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_FUNCTION_VARIANTS ) {
-            const linkedit_data_command* leDataCmd = (linkedit_data_command*)cmd;
-            fileOffset  = leDataCmd->dataoff;
-            stop        = true;
-        }
-    });
-    if ( fileOffset == 0 )
-        return false;
-
-    runtimeOffset = fileOffset + zerofillExpansionAmount();
-    return true;
-}
-
-
-CString Header::libOrdinalName(int libOrdinal) const
-{
-    switch ( libOrdinal) {
-        case BIND_SPECIAL_DYLIB_SELF:
-            return "<this-image>";
-        case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
-            return "<main-executable>";
-        case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
-            return "<flat-namespace>";
-        case BIND_SPECIAL_DYLIB_WEAK_LOOKUP:
-            return "<weak-def-coalesce>";
-        default:
-            if ( (libOrdinal > 0) && (libOrdinal <= this->linkedDylibCount()) )
-                return this->linkedDylibLoadPath(libOrdinal-1);
-            break;
-    }
-    return "<invalid-lib-ordinal>";
-}
-
-const load_command* Header::findLoadCommand(uint32_t& index, bool (^predicate)(const load_command* lc)) const
-{
-    index = 0;
-    __block const load_command* lc = nullptr;
-    __block uint32_t currentIndex = 0;
-    forEachLoadCommandSafe(^(const load_command *cmd, bool &stop) {
-        if ( predicate(cmd) ) {
-            index = currentIndex;
-            lc = cmd;
-            stop = true;
-        }
-
-        ++currentIndex;
-    });
-
-    return lc;
-}
-
-void Header::findLoadCommandRange(uint32_t& index, uint32_t& endIndex, bool (^predicate)(const load_command* lc)) const
-{
-    index = UINT32_MAX;
-    endIndex = UINT32_MAX;
-    __block uint32_t currentIndex = 0;
-    forEachLoadCommandSafe(^(const load_command *cmd, bool &stop) {
-        if ( predicate(cmd) ) {
-            if ( index == UINT32_MAX ) { // first match, set indices
-                index    = currentIndex;
-                endIndex = index+1;
-            } else {
-                assert(endIndex == currentIndex); // range must be contiguous
-                ++endIndex;
-            }
-        } else if (index != UINT32_MAX) {
-            // not a match, so stop looking if the first match had already been found
-            stop = true;
-        }
-
-        ++currentIndex;
-    });
-
-    // clear index if none matched
-    if ( index == UINT32_MAX ) {
-        index = 0;
-        endIndex = 0;
-    }
-}
-
-
-const dylib_command* Header::isDylibLoadCommand(const load_command* lc)
-{
-    switch (lc->cmd) {
-        case LC_LOAD_DYLIB:
-        case LC_LOAD_WEAK_DYLIB:
-        case LC_LOAD_UPWARD_DYLIB:
-        case LC_REEXPORT_DYLIB:
-            return (dylib_command*)lc;
-        default:
-            return nullptr;
-    }
-}
-
-uint32_t Header::sizeForLinkedDylibCommand(const char *path, LinkedDylibAttributes depAttrs, uint32_t& traditionalCmd) const
-{
-    traditionalCmd = 0;
-    if (      depAttrs == LinkedDylibAttributes::regular )
-        traditionalCmd = LC_LOAD_DYLIB;
-    else if ( depAttrs == LinkedDylibAttributes::justWeakLink )
-        traditionalCmd = LC_LOAD_WEAK_DYLIB;
-    else if ( depAttrs == LinkedDylibAttributes::justUpward )
-        traditionalCmd = LC_LOAD_UPWARD_DYLIB;
-    else if ( depAttrs == LinkedDylibAttributes::justReExport )
-        traditionalCmd = LC_REEXPORT_DYLIB;
-
-    if ( traditionalCmd )
-        return pointerAligned((uint32_t)(sizeof(dylib_command) + strlen(path) + 1));
-
-    // use new style load command
-    return pointerAligned((uint32_t)(sizeof(dylib_use_command) + strlen(path) + 1));
-}
-
-
-const thread_command* Header::unixThreadLoadCommand() const {
-    __block const thread_command* command = nullptr;
-    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_UNIXTHREAD ) {
-            command = (const thread_command*)cmd;
-            stop = true;
-        }
-    });
-    return command;
-}
-
-
-const char* Header::protectionString(uint32_t flags, char str[8])
-{
-    str[0] = (flags & VM_PROT_READ)    ? 'r' : '.';
-    str[1] = (flags & VM_PROT_WRITE)   ? 'w' : '.';
-    str[2] = (flags & VM_PROT_EXECUTE) ? 'x' : '.';
-    str[3] = '\0';
-    return str;
-}
-
-static const char* sectionAttributes(uint32_t sectFlags)
-{
-    if ( sectFlags == 0 )
-        return "";
-
-    switch ( sectFlags & SECTION_TYPE ) {
-        case S_ZEROFILL:
-            return "S_ZEROFILL";
-        case S_CSTRING_LITERALS:
-            return "S_CSTRING_LITERALS";
-        case S_NON_LAZY_SYMBOL_POINTERS:
-            return "S_NON_LAZY_SYMBOL_POINTERS";
-        case S_THREAD_LOCAL_VARIABLES:
-            return "S_THREAD_LOCAL_VARIABLES";
-        case S_INIT_FUNC_OFFSETS:
-            return "S_INIT_FUNC_OFFSETS";
-        case S_SYMBOL_STUBS:
-            return "S_SYMBOL_STUBS";
-    }
-
-    if ( sectFlags == S_ATTR_PURE_INSTRUCTIONS )
-        return "S_ATTR_PURE_INSTRUCTIONS";
-    if ( sectFlags == (S_ATTR_PURE_INSTRUCTIONS|S_ATTR_SOME_INSTRUCTIONS) )
-        return "S_ATTR_PURE_INSTRUCTIONS|S_ATTR_SOME_INSTRUCTIONS";
-
-    return "";
-}
-
-void Header::printLoadCommands(FILE* out, unsigned indentLevel) const
-{
-    char indentBuffer[indentLevel+2];
-    for (unsigned i=0; i < indentLevel; ++i)
-        indentBuffer[i] = ' ';
-    indentBuffer[indentLevel] = '\0';
-    const char* indent = indentBuffer;
-    __block uint32_t lcIndex = 0;
-    this->forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        fprintf(out, "%sLoad command #%d\n", indent, lcIndex);
-        const dylib_command*                dylibCmd;
-        const segment_command*              seg32Cmd;
-        const segment_command_64*           seg64Cmd;
-        const linkedit_data_command*        leDataCmd;
-        const symtab_command*               symTabCmd;
-        const dysymtab_command*             dynSymCmd;
-        const uuid_command*                 uuidCmd;
-        const version_min_command*          versMinCmd;
-        const build_version_command*        buildVerCmd;
-        const rpath_command*                rpathCmd;
-        const dylinker_command*             dyldEnvCmd;
-        const entry_point_command*          mainCmd;
-        const sub_client_command*           subClientCmd;
-        const routines_command*             inits32Cmd;
-        const routines_command_64*          inits64Cmd;
-        const encryption_info_command*      encrypt32Cmd;
-        const encryption_info_command_64*   encrypt64Cmd;
-        const dyld_info_command*            dyldInfoCmd;
-        const target_triple_command*        tripleCmd;
-        char                                versStr[64];
-        char                                maxProtStr[8];
-        char                                initProtStr[8];
-        char                                linkAttrStr[64];
-        uuid_string_t                       uuidStr;
-        const section_64*                   sections64;
-        const section*                      sections32;
-        LinkedDylibAttributes               linkAttrs;
-        const uint8_t*                      bytes;
-        const uint32_t*                     words;
-        uint64_t                            vers64;
-        switch (cmd->cmd) {
-            case LC_SEGMENT:
-                seg32Cmd = (segment_command*)cmd;
-                fprintf(out, "%s             cmd: LC_SEGMENT\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",       indent, cmd->cmdsize);
-                fprintf(out, "%s         segname: \"%.*s\"\n",   indent, (int)name16(seg32Cmd->segname).size(), name16(seg32Cmd->segname).data());
-                fprintf(out, "%s          vmaddr: 0x%08X\n",     indent, seg32Cmd->vmaddr);
-                fprintf(out, "%s          vmsize: 0x%08X\n",     indent, seg32Cmd->vmsize);
-                fprintf(out, "%s         fileoff: 0x%08X\n",     indent, seg32Cmd->fileoff);
-                fprintf(out, "%s        filesize: 0x%08X\n",     indent, seg32Cmd->filesize);
-                fprintf(out, "%s         maxprot: %s\n",         indent, Header::protectionString(seg32Cmd->maxprot, maxProtStr));
-                fprintf(out, "%s        initprot: %s\n",         indent, Header::protectionString(seg32Cmd->initprot, initProtStr));
-                if ( seg32Cmd->flags & SG_READ_ONLY )
-                    fprintf(out, "%s          flags: SG_READ_ONLY\n",indent);
-                fprintf(out, "%s          nsects: %d\n",         indent, seg32Cmd->nsects);
-                sections32 = (section*)((char*)seg32Cmd + sizeof(struct segment_command));
-                for (int i=0; i < seg32Cmd->nsects; ++i) {
-                    fprintf(out, "%s        sections[%d].sectname: \"%.*s\"\n", indent, i, (int)name16(sections32[i].sectname).size(), name16(sections32[i].sectname).data());
-                    fprintf(out, "%s        sections[%d].addr:     0x%08X\n",   indent, i, sections32[i].addr);
-                    fprintf(out, "%s        sections[%d].size:     0x%08X\n",   indent, i, sections32[i].size);
-                    if ( sections32[i].offset != 0 )
-                        fprintf(out, "%s        sections[%d].offset:   0x%08X\n",  indent, i, sections32[i].offset);
-                    fprintf(out, "%s        sections[%d].align:    2^%d\n",        indent, i, sections32[i].align);
-                    if ( sections32[i].flags != 0 )
-                        fprintf(out, "%s        sections[%d].type:      %s\n",     indent, i, sectionAttributes(sections32[i].flags));
-                    if ( sections32[i].reserved1 != 0 )
-                        fprintf(out, "%s        sections[%d].reserved1: %d\n",     indent, i, sections32[i].reserved1);
-                    if ( sections32[i].reserved2 != 0 )
-                        fprintf(out, "%s        sections[%d].reserved2: %d\n",     indent, i, sections32[i].reserved2);
-                }
-                break;
-            case LC_SYMTAB:
-                symTabCmd = (symtab_command*)cmd;
-                fprintf(out, "%s             cmd: LC_SYMTAB\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",      indent, cmd->cmdsize);
-                fprintf(out, "%s          symoff: 0x%X\n",      indent, symTabCmd->symoff);
-                fprintf(out, "%s           nsyms: %d\n",        indent, symTabCmd->nsyms);
-                fprintf(out, "%s          stroff: 0x%X\n",      indent, symTabCmd->stroff);
-                fprintf(out, "%s         strsize: 0x%X\n",      indent, symTabCmd->strsize);
-                break;
-            case LC_UNIXTHREAD:
-                words = (uint32_t*)cmd;
-                fprintf(out, "%s             cmd: LC_UNIXTHREAD\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",          indent, cmd->cmdsize);
-                if ( words[2] == 6 ) { // ARM_THREAD_STATE64
-                    fprintf(out, "%s              pc: 0x%llX\n",        indent, *(uint64_t*)(&words[68]));
-                }
-                else if ( words[2] == 4 ) { // x86_THREAD_STATE64
-                    fprintf(out, "%s              pc: 0x%llX\n",        indent, *(uint64_t*)(&words[36]));
-                }
-                else if ( words[2] == 1 ) { // x86_THREAD_STATE
-                    fprintf(out, "%s              pc: 0x%X\n",          indent, words[14]);
-                }
-                break;
-            case LC_DYSYMTAB:
-                dynSymCmd = (dysymtab_command*)cmd;
-                fprintf(out, "%s             cmd: LC_DYSYMTAB\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",        indent, cmd->cmdsize);
-                fprintf(out, "%s       ilocalsym: %d\n",          indent, dynSymCmd->ilocalsym);
-                fprintf(out, "%s       nlocalsym: %d\n",          indent, dynSymCmd->nlocalsym);
-                fprintf(out, "%s      iextdefsym: %d\n",          indent, dynSymCmd->iextdefsym);
-                fprintf(out, "%s      nextdefsym: %d\n",          indent, dynSymCmd->nextdefsym);
-                fprintf(out, "%s       iundefsym: %d\n",          indent, dynSymCmd->iundefsym);
-                fprintf(out, "%s       nundefsym: %d\n",          indent, dynSymCmd->nundefsym);
-                fprintf(out, "%s  indirectsymoff: 0x%08X\n",      indent, dynSymCmd->indirectsymoff);
-                fprintf(out, "%s   nindirectsyms: %d\n",          indent, dynSymCmd->nindirectsyms);
-                fprintf(out, "%s       ilocalsym: %d\n",          indent, dynSymCmd->ilocalsym);
-               break;
-            case LC_LOAD_DYLIB:
-                dylibCmd = (dylib_command*)cmd;
-                linkAttrs = loadCommandToDylibKind(dylibCmd);
-                fprintf(out, "%s             cmd: LC_LOAD_DYLIB\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",          indent, cmd->cmdsize);
-                fprintf(out, "%s            name: \"%s\"\n",        indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
-                if ( linkAttrs.raw != 0 ) {
-                    linkAttrs.toString(linkAttrStr);
-                    fprintf(out, "%s          attrs: %s\n",             indent, linkAttrStr);
-                }
-                fprintf(out, "%s        cur-vers: %s\n",            indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
-                fprintf(out, "%s     compat-vers: %s\n",            indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
-                break;
-            case LC_ID_DYLIB:
-                dylibCmd = (dylib_command*)cmd;
-                fprintf(out, "%s             cmd: LC_ID_DYLIB\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",        indent, cmd->cmdsize);
-                fprintf(out, "%s            name: \"%s\"\n",      indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
-                fprintf(out, "%s        cur-vers: %s\n",          indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
-                fprintf(out, "%s     compat-vers: %s\n",          indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
-                break;
-            case LC_LOAD_DYLINKER:
-                dylibCmd = (dylib_command*)cmd;
-                fprintf(out, "%s             cmd: LC_LOAD_DYLINKER\n",  indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s            name: \"%s\"\n",            indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
-                break;
-            case LC_ID_DYLINKER:
-                dylibCmd = (dylib_command*)cmd;
-                fprintf(out, "%s             cmd: LC_ID_DYLINKER\n",    indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s            name: \"%s\"\n",            indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
-                break;
-            case LC_ROUTINES:
-                inits32Cmd = (routines_command*)cmd;
-                fprintf(out, "%s             cmd: LC_ROUTINES\n",       indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s    init_address: 0x%X\n",              indent, inits32Cmd->init_address);
-                break;
-            case LC_SUB_FRAMEWORK:
-                subClientCmd = (sub_client_command*)cmd;
-                fprintf(out, "%s             cmd: LC_SUB_FRAMEWORK\n",  indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s        umbrella: \"%s\"\n",            indent, (char*)subClientCmd + subClientCmd->client.offset);
-                break;
-            case LC_SUB_UMBRELLA:
-                subClientCmd = (sub_client_command*)cmd;
-                fprintf(out, "%s             cmd: LC_SUB_UMBRELLA\n",   indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s    sub_umbrella: \"%s\"\n",            indent, (char*)subClientCmd + subClientCmd->client.offset);
-                break;
-            case LC_SUB_CLIENT:
-                subClientCmd = (sub_client_command*)cmd;
-                fprintf(out, "%s             cmd: LC_SUB_CLIENT\n",     indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s          client: \"%s\"\n",            indent, (char*)subClientCmd + subClientCmd->client.offset);
-                break;
-            case LC_SUB_LIBRARY:
-                subClientCmd = (sub_client_command*)cmd;
-                fprintf(out, "%s             cmd: LC_SUB_LIBRARY\n",    indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s     sub_library: \"%s\"\n",            indent, (char*)subClientCmd + subClientCmd->client.offset);
-                break;
-            case LC_LOAD_WEAK_DYLIB:
-                dylibCmd = (dylib_command*)cmd;
-                fprintf(out, "%s             cmd: LC_LOAD_WEAK_DYLIB\n",indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s            name: \"%s\"\n",            indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
-                fprintf(out, "%s        cur-vers: %s\n",                indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
-                fprintf(out, "%s     compat-vers: %s\n",                indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
-                break;
-            case LC_SEGMENT_64:
-                seg64Cmd = (segment_command_64*)cmd;
-                fprintf(out, "%s             cmd: LC_SEGMENT_64\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",          indent, cmd->cmdsize);
-                fprintf(out, "%s         segname: \"%.*s\"\n",      indent, (int)name16(seg64Cmd->segname).size(), name16(seg64Cmd->segname).data());
-                fprintf(out, "%s          vmaddr: 0x%016llX\n",     indent, seg64Cmd->vmaddr);
-                fprintf(out, "%s          vmsize: 0x%016llX\n",     indent, seg64Cmd->vmsize);
-                fprintf(out, "%s         fileoff: 0x%08llX\n",      indent, seg64Cmd->fileoff);
-                fprintf(out, "%s        filesize: 0x%08llX\n",      indent, seg64Cmd->filesize);
-                fprintf(out, "%s         maxprot: %s\n",            indent, Header::protectionString(seg64Cmd->initprot, maxProtStr));
-                fprintf(out, "%s        initprot: %s\n",            indent, Header::protectionString(seg64Cmd->initprot, initProtStr));
-                if ( seg64Cmd->flags & SG_READ_ONLY )
-                    fprintf(out, "%s          flags: SG_READ_ONLY\n",indent);
-                fprintf(out, "%s          nsects: %d\n",            indent, seg64Cmd->nsects);
-                sections64 = (section_64*)((char*)seg64Cmd + sizeof(struct segment_command_64));
-                for (int i=0; i < seg64Cmd->nsects; ++i) {
-                    fprintf(out, "%s        sections[%d].sectname: \"%.*s\"\n",    indent, i, (int)name16(sections64[i].sectname).size(), name16(sections64[i].sectname).data());
-                    fprintf(out, "%s        sections[%d].addr:     0x%016llX\n",   indent, i, sections64[i].addr);
-                    fprintf(out, "%s        sections[%d].size:     0x%016llX\n",   indent, i, sections64[i].size);
-                    if ( sections64[i].offset != 0 )
-                        fprintf(out, "%s        sections[%d].offset:   0x%08X\n",      indent, i, sections64[i].offset);
-                    fprintf(out, "%s        sections[%d].align:    2^%d\n",        indent, i, sections64[i].align);
-                    if ( sections64[i].flags != 0 )
-                        fprintf(out, "%s        sections[%d].type:      %s\n",     indent, i, sectionAttributes(sections64[i].flags));
-                    if ( sections64[i].reserved1 != 0 )
-                        fprintf(out, "%s        sections[%d].reserved1: %d\n",     indent, i, sections64[i].reserved1);
-                    if ( sections64[i].reserved2 != 0 )
-                        fprintf(out, "%s        sections[%d].reserved2: %d\n",     indent, i, sections64[i].reserved2);
-                }
-                break;
-            case LC_ROUTINES_64:
-                inits64Cmd = (routines_command_64*)cmd;
-                fprintf(out, "%s             cmd: LC_ROUTINES\n",       indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s    init_address: 0x%llX\n",            indent, inits64Cmd->init_address);
-                break;
-            case LC_UUID:
-                uuidCmd = (uuid_command*)cmd;
-                uuid_unparse_upper(uuidCmd->uuid, uuidStr);
-                fprintf(out, "%s             cmd: LC_UUID\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",    indent, cmd->cmdsize);
-                fprintf(out, "%s            uuid: %s\n",      indent, uuidStr);
-               break;
-            case LC_RPATH:
-                rpathCmd = (rpath_command*)cmd;
-                fprintf(out, "%s             cmd: LC_RPATH\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",     indent, cmd->cmdsize);
-                fprintf(out, "%s           rpath:\"%s\"\n",    indent, (char*)cmd + rpathCmd->path.offset);
-                break;
-            case LC_CODE_SIGNATURE:
-                leDataCmd = (linkedit_data_command*)cmd;
-                fprintf(out, "%s             cmd: LC_CODE_SIGNATURE\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s         dataoff: 0x%X\n",              indent, leDataCmd->dataoff);
-                fprintf(out, "%s        datasize: 0x%X\n",              indent, leDataCmd->datasize);
-                break;
-            case LC_SEGMENT_SPLIT_INFO:
-                leDataCmd = (linkedit_data_command*)cmd;
-                fprintf(out, "%s             cmd: LC_SEGMENT_SPLIT_INFO\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                  indent, cmd->cmdsize);
-                fprintf(out, "%s         dataoff: 0x%X\n",                  indent, leDataCmd->dataoff);
-                fprintf(out, "%s        datasize: 0x%X\n",                  indent, leDataCmd->datasize);
-                break;
-            case LC_REEXPORT_DYLIB:
-                dylibCmd = (dylib_command*)cmd;
-                fprintf(out, "%s             cmd: LC_REEXPORT_DYLIB\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s            name: \"%s\"\n",            indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
-                fprintf(out, "%s        cur-vers: %s\n",                indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
-                fprintf(out, "%s     compat-vers: %s\n",                indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
-                break;
-            case LC_ENCRYPTION_INFO:
-                encrypt32Cmd = (encryption_info_command*)cmd;
-                fprintf(out, "%s             cmd: LC_ENCRYPTION_INFO\n",indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s        cryptoff: 0x%X\n",              indent, encrypt32Cmd->cryptoff);
-                fprintf(out, "%s       cryptsize: 0x%X\n",              indent, encrypt32Cmd->cryptsize);
-                fprintf(out, "%s         cryptid: 0x%X\n",              indent, encrypt32Cmd->cryptid);
-                break;
-            case LC_DYLD_INFO:
-            case LC_DYLD_INFO_ONLY:
-                dyldInfoCmd = (dyld_info_command*)cmd;
-                fprintf(out, "%s             cmd: LC_DYLD_INFO%s\n",     indent, (cmd->cmd == LC_DYLD_INFO_ONLY ? "_ONLY" : ""));
-                fprintf(out, "%s         cmdsize: 0x%X\n",               indent, cmd->cmdsize);
-                fprintf(out, "%s      rebase_off: 0x%X\n",               indent, dyldInfoCmd->rebase_off);
-                fprintf(out, "%s     rebase_size: 0x%X\n",               indent, dyldInfoCmd->rebase_size);
-                fprintf(out, "%s        bind_off: 0x%X\n",               indent, dyldInfoCmd->bind_off);
-                fprintf(out, "%s       bind_size: 0x%X\n",               indent, dyldInfoCmd->bind_size);
-                fprintf(out, "%s   weak_bind_off: 0x%X\n",               indent, dyldInfoCmd->weak_bind_off);
-                fprintf(out, "%s  weak_bind_size: 0x%X\n",               indent, dyldInfoCmd->weak_bind_size);
-                fprintf(out, "%s   lazy_bind_off: 0x%X\n",               indent, dyldInfoCmd->lazy_bind_off);
-                fprintf(out, "%s  lazy_bind_size: 0x%X\n",               indent, dyldInfoCmd->lazy_bind_size);
-                fprintf(out, "%s      export_off: 0x%X\n",               indent, dyldInfoCmd->export_off);
-                fprintf(out, "%s     export_size: 0x%X\n",               indent, dyldInfoCmd->export_size);
-                break;
-            case LC_LOAD_UPWARD_DYLIB:
-                dylibCmd = (dylib_command*)cmd;
-                fprintf(out, "%s             cmd: LC_LOAD_UPWARD_DYLIB\n",  indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                  indent, cmd->cmdsize);
-                fprintf(out, "%s            name: \"%s\"\n",                indent, (char*)dylibCmd + dylibCmd->dylib.name.offset);
-                fprintf(out, "%s        cur-vers: %s\n",                    indent, Version32(dylibCmd->dylib.current_version).toString(versStr));
-                fprintf(out, "%s     compat-vers: %s\n",                    indent, Version32(dylibCmd->dylib.compatibility_version).toString(versStr));
-                break;
-            case LC_VERSION_MIN_MACOSX:
-                versMinCmd = (version_min_command*)cmd;
-                fprintf(out, "%s             cmd: LC_VERSION_MIN_MACOSX\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                  indent, cmd->cmdsize);
-                fprintf(out, "%s          min-OS: %s\n",                    indent, Version32(versMinCmd->version).toString(versStr));
-                fprintf(out, "%s             sdk: %s\n",                    indent, Version32(versMinCmd->sdk).toString(versStr));
-                break;
-            case LC_VERSION_MIN_IPHONEOS:
-                versMinCmd = (version_min_command*)cmd;
-                fprintf(out, "%s             cmd: LC_VERSION_MIN_IPHONEOS\n",indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                   indent, cmd->cmdsize);
-                fprintf(out, "%s          min-OS: %s\n",                     indent, Version32(versMinCmd->version).toString(versStr));
-                fprintf(out, "%s             sdk: %s\n",                     indent, Version32(versMinCmd->sdk).toString(versStr));
-                break;
-            case LC_FUNCTION_STARTS:
-                leDataCmd = (linkedit_data_command*)cmd;
-                fprintf(out, "%s             cmd: LC_FUNCTION_STARTS\n",indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s         dataoff: 0x%X\n",              indent, leDataCmd->dataoff);
-                fprintf(out, "%s        datasize: 0x%X\n",              indent, leDataCmd->datasize);
-                break;
-            case LC_DYLD_ENVIRONMENT:
-                dyldEnvCmd = (dylinker_command*)cmd;
-                fprintf(out, "%s             cmd: LC_DYLD_ENVIRONMENT\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",     indent, cmd->cmdsize);
-                fprintf(out, "%s            name:\"%s\"\n",    indent, (char*)dyldEnvCmd + dyldEnvCmd->name.offset);
-                break;
-            case LC_MAIN:
-                mainCmd = (entry_point_command*)cmd;
-                fprintf(out, "%s             cmd: LC_MAIN\n",       indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",          indent, cmd->cmdsize);
-                fprintf(out, "%s        entryoff: 0x%llX\n",        indent, mainCmd->entryoff);
-                if ( mainCmd->stacksize != 0 )
-                    fprintf(out, "%s       stacksize: 0x%llX\n",        indent, mainCmd->stacksize);
-                break;
-            case LC_DATA_IN_CODE:
-                leDataCmd = (linkedit_data_command*)cmd;
-                fprintf(out, "%s             cmd: LC_DATA_IN_CODE\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",            indent, cmd->cmdsize);
-                fprintf(out, "%s         dataoff: 0x%X\n",            indent, leDataCmd->dataoff);
-                fprintf(out, "%s        datasize: 0x%X\n",            indent, leDataCmd->datasize);
-                break;
-            case LC_SOURCE_VERSION:
-                memcpy(&vers64, ((uint8_t*)cmd)+8, 8);  // in 32-bit arches load command may not be 8-byte aligned, so can't use source_version_command.version.
-                fprintf(out, "%s             cmd: LC_SOURCE_VERSION\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s         version: %s\n",                indent, Version64(vers64).toString(versStr));
-                break;
-            case LC_ENCRYPTION_INFO_64:
-                encrypt64Cmd = (encryption_info_command_64*)cmd;
-                fprintf(out, "%s             cmd: LC_ENCRYPTION_INFO\n",indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s        cryptoff: 0x%X\n",              indent, encrypt64Cmd->cryptoff);
-                fprintf(out, "%s       cryptsize: 0x%X\n",              indent, encrypt64Cmd->cryptsize);
-                fprintf(out, "%s         cryptid: 0x%X\n",              indent, encrypt64Cmd->cryptid);
-                break;
-            case LC_VERSION_MIN_TVOS:
-                versMinCmd = (version_min_command*)cmd;
-                fprintf(out, "%s             cmd: LC_VERSION_MIN_TVOS\n",indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",               indent, cmd->cmdsize);
-                fprintf(out, "%s          min-OS: %s\n",                 indent, Version32(versMinCmd->version).toString(versStr));
-                fprintf(out, "%s             sdk: %s\n",                 indent, Version32(versMinCmd->sdk).toString(versStr));
-                break;
-            case LC_VERSION_MIN_WATCHOS:
-                versMinCmd = (version_min_command*)cmd;
-                fprintf(out, "%s             cmd: LC_VERSION_MIN_WATCHOS\n",indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                  indent, cmd->cmdsize);
-                fprintf(out, "%s          min-OS: %s\n",                    indent, Version32(versMinCmd->version).toString(versStr));
-                fprintf(out, "%s             sdk: %s\n",                    indent, Version32(versMinCmd->sdk).toString(versStr));
-                break;
-            case LC_BUILD_VERSION:
-                buildVerCmd = (build_version_command*)cmd;
-                fprintf(out, "%s             cmd: LC_BUILD_VERSION\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                  indent, cmd->cmdsize);
-                fprintf(out, "%s        platform: %s\n",                    indent, Platform(buildVerCmd->platform).name().c_str());
-                fprintf(out, "%s          min-OS: %s\n",                    indent, Version32(buildVerCmd->minos).toString(versStr));
-                fprintf(out, "%s             sdk: %s\n",                    indent, Version32(buildVerCmd->sdk).toString(versStr));
-                if ( buildVerCmd->ntools != 0 ) {
-                    const build_tool_version*  toolVersions = (build_tool_version*)((uint8_t*)cmd + sizeof(build_version_command));
-                    for (uint32_t i=0; i < buildVerCmd->ntools; ++i ) {
-                        const char* toolName = "unknown";
-                        switch ( toolVersions[i].tool ) {
-                            case TOOL_CLANG:
-                                toolName = "clang";
-                                break;
-                            case TOOL_SWIFT:
-                                toolName = "swiftc";
-                                break;
-                            case TOOL_LD:
-                                toolName = "ld";
-                                break;
-                            case TOOL_LLD:
-                                toolName = "lld";
-                                break;
-                        }
-                        fprintf(out, "%s         tool[%d].name:    %s\n",                    indent, i, toolName);
-                        fprintf(out, "%s         tool[%d].version: %s\n",                    indent, i, Version32(toolVersions[i].version).toString(versStr));
-                    }
-                }
-                break;
-            case LC_DYLD_EXPORTS_TRIE:
-                leDataCmd = (linkedit_data_command*)cmd;
-                fprintf(out, "%s             cmd: LC_DYLD_EXPORTS_TRIE\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                 indent, cmd->cmdsize);
-                fprintf(out, "%s         dataoff: 0x%X\n",                 indent, leDataCmd->dataoff);
-                fprintf(out, "%s        datasize: 0x%X\n",                 indent, leDataCmd->datasize);
-                break;
-            case LC_DYLD_CHAINED_FIXUPS:
-                leDataCmd = (linkedit_data_command*)cmd;
-                fprintf(out, "%s             cmd: LC_DYLD_CHAINED_FIXUPS\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                   indent, cmd->cmdsize);
-                fprintf(out, "%s         dataoff: 0x%X\n",                   indent, leDataCmd->dataoff);
-                fprintf(out, "%s        datasize: 0x%X\n",                   indent, leDataCmd->datasize);
-                break;
-            case LC_FUNCTION_VARIANTS:
-                leDataCmd = (linkedit_data_command*)cmd;
-                fprintf(out, "%s             cmd: LC_FUNCTION_VARIANTS\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",            indent, cmd->cmdsize);
-                fprintf(out, "%s         dataoff: 0x%X\n",            indent, leDataCmd->dataoff);
-                fprintf(out, "%s        datasize: 0x%X\n",            indent, leDataCmd->datasize);
-                break;
-            case LC_FUNCTION_VARIANT_FIXUPS:
-                leDataCmd = (linkedit_data_command*)cmd;
-                fprintf(out, "%s             cmd: LC_FUNCTION_VARIANT_FIXUPS\n", indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",            indent, cmd->cmdsize);
-                fprintf(out, "%s         dataoff: 0x%X\n",            indent, leDataCmd->dataoff);
-                fprintf(out, "%s        datasize: 0x%X\n",            indent, leDataCmd->datasize);
-                break;
-            case LC_TARGET_TRIPLE:
-                tripleCmd = (target_triple_command*)cmd;
-                fprintf(out, "%s             cmd: LC_TARGET_TRIPLE\n",  indent);
-                fprintf(out, "%s         cmdsize: 0x%X\n",              indent, cmd->cmdsize);
-                fprintf(out, "%s          triple:\"%s\"\n",             indent, (char*)cmd + tripleCmd->triple.offset);
-                break;
-            default:
-                bytes = (uint8_t*)cmd + 8;
-                fprintf(out, "%s             cmd: 0x%X\n",                   indent, cmd->cmd);
-                fprintf(out, "%s         cmdsize: 0x%X\n",                   indent, cmd->cmdsize);
-                fprintf(out, "%s           bytes: ",                         indent);
-                for (int i=0; i < cmd->cmdsize; ++i) {
-                    if ( ((i & 0xF) == 0) && (i != 0) )
-                        fprintf(out, "%s                  ", indent);
-                    fprintf(out, "%02X ", bytes[i]);
-                    if ( (i & 0xF) == 0xF )
-                        fprintf(out, "\n");
-                }
-                if ( (cmd->cmdsize & 0xF) != 0x0 )
-                    fprintf(out, "\n");
-                break;
-        }
-        ++lcIndex;
-    });
-
-    
-}
-
-bool Header::loadableIntoProcess(Platform processPlatform, CString path, bool internalInstall) const
-{
-    if ( this->builtForPlatform(processPlatform) )
-        return true;
-
-    // Some host macOS dylibs can be loaded into simulator processes
-    if (processPlatform.isSimulator() && this->builtForPlatform(Platform::macOS)) {
-        static constinit CString const macOSHost[] = {
-            "/usr/lib/system/libsystem_kernel.dylib",
-            "/usr/lib/system/libsystem_platform.dylib",
-            "/usr/lib/system/libsystem_pthread.dylib",
-            "/usr/lib/system/libsystem_platform_debug.dylib",
-            "/usr/lib/system/libsystem_pthread_debug.dylib",
-            "/usr/lib/system/host/liblaunch_sim.dylib",
-        };
-        if ( std::ranges::any_of(macOSHost, [&](const CString& libPath) { return libPath == path; }) ) {
-            return true;
-        }
-    }
-
-    // If this is being called on main executable where we expect a macOS program, Catalyst programs are also runnable
-    if ( this->isMainExecutable() && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::macCatalyst, true) )
-        return true;
-#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
-    if ( this->isMainExecutable() && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOS, true) )
-        return true;
-#endif
-
-    // allow iOS executables to use visionOS dylibs
-    if ( (processPlatform == Platform::iOS) && this->builtForPlatform(Platform::visionOS, true) )
-        return true;
-
-    // allow iOS_Sim executables to use visionOS_Sim dylibs
-    if ( (processPlatform == Platform::iOS_simulator) && this->builtForPlatform(Platform::visionOS_simulator, true) )
-        return true;
-
-    bool iOSonMac = (processPlatform == Platform::macCatalyst);
-#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
-    // allow iOS binaries in iOSApp
-    if ( processPlatform == Platform::iOS ) {
-        // can load Catalyst binaries into iOS process
-        if ( this->builtForPlatform(Platform::macCatalyst) )
-            return true;
-        iOSonMac = true;
-    }
-#endif
-    // macOS dylibs can be loaded into iOSMac processes
-    if ( iOSonMac && this->builtForPlatform(Platform::macOS, true) )
-        return true;
-
-    return false;
-}
-
-bool Header::hasPlusLoadMethod() const
-{
-    __block bool result = false;
-    // in new objc runtime compiler puts classes/categories with +load method in specical section
-    forEachSection(^(const Header::SectionInfo& info, bool& stop) {
-        if ( !info.segmentName.starts_with("__DATA") )
-            return;
-        if ( (info.sectionName == "__objc_nlclslist") || (info.sectionName == "__objc_nlcatlist") ) {
-            result = true;
-            stop = true;
-        }
-    });
-    return result;
-}
-
-// returns empty string if no triple specified
-CString Header::targetTriple() const
-{
-    __block CString result;
-    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_TARGET_TRIPLE ) {
-            const char* targetTriple = (char*)cmd + ((struct target_triple_command*)cmd)->triple.offset;
-            result = targetTriple;
-        }
-    });
-    return result;
-}
-
-void Header::forEachSingletonPatch(void (^handler)(uint64_t runtimeOffset)) const
-{
-    uint32_t ptrSize = pointerSize();
-    uint32_t elementSize = (2 * ptrSize);
-    uint64_t loadAddress = preferredLoadAddress();
-    forEachSection(^(const SectionInfo &sectInfo, bool &stop) {
-        if ( sectInfo.sectionName != "__const_cfobj2" )
-            return;
-        stop = true;
-
-        if ( (sectInfo.size % elementSize) != 0 ) {
-            return;
-        }
-
-        if ( sectInfo.reserved2 != elementSize ) {
-            // The linker must have rejected one or more of the elements in the section, so
-            // didn't set the reserved2 to let us patch
-            return;
-        }
-
-        for ( uint64_t offset = 0; offset != sectInfo.size; offset += elementSize ) {
-            uint64_t targetRuntimeOffset = (sectInfo.address + offset) - loadAddress;
-            handler(targetRuntimeOffset);
-        }
-    });
-}
-
-bool Header::hasObjCMessageReferences() const {
-    uint64_t sectionRuntimeOffset, sectionSize;
-    return findObjCDataSection("__objc_msgrefs", sectionRuntimeOffset, sectionSize);
-}
-
-bool Header::findObjCDataSection(CString sectionName, uint64_t& sectionRuntimeOffset, uint64_t& sectionSize) const
-{
-    uint64_t baseAddress = preferredLoadAddress();
-
-    __block bool foundSection = false;
-    forEachSection(^(const Header::SectionInfo& sectInfo, bool& stop) {
-        if ( !sectInfo.segmentName.starts_with("__DATA") )
-            return;
-        if ( sectInfo.sectionName != sectionName )
-            return;
-        foundSection         = true;
-        sectionRuntimeOffset = sectInfo.address - baseAddress;
-        sectionSize          = sectInfo.size;
-        stop                 = true;
-    });
-    return foundSection;
-}
 
 uint32_t Header::segmentCount() const
 {
@@ -2316,32 +1384,23 @@
     return count;
 }
 
-uint32_t Header::loadCommandsFreeSpace() const
-{
-    __block uint32_t firstSectionFileOffset = UINT32_MAX;
-    __block uint32_t firstSegmentFileOffset = UINT32_MAX;
-    forEachSection(^(const SectionInfo& sectInfo, bool &stop) {
-        firstSectionFileOffset = sectInfo.fileOffset;
-        firstSegmentFileOffset = segmentFileOffset(sectInfo.segIndex);
-        stop = true;
-    });
-
-    // no segment load commands yet, so explicit size limit
-    if ( firstSegmentFileOffset == UINT32_MAX )
-        return UINT32_MAX;
-
-    uint32_t headerSize = (mh.magic == MH_MAGIC_64) ? sizeof(mach_header_64) : sizeof(mach_header);
-    uint32_t existSpaceUsed = mh.sizeofcmds + headerSize;
-    return firstSectionFileOffset - firstSegmentFileOffset - existSpaceUsed;
-}
-
 uint64_t Header::preferredLoadAddress() const
 {
     __block uint64_t textVmAddr = 0;
-    forEachSegment(^(const SegmentInfo &info, bool &stop) {
-        if ( info.segmentName == "__TEXT" ) {
-            textVmAddr = info.vmaddr;
-            stop = true;
+    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* segCmd = (segment_command_64*)cmd;
+            if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
+                textVmAddr = segCmd->vmaddr;
+                stop = true;
+            }
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* segCmd = (segment_command*)cmd;
+            if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
+                textVmAddr = segCmd->vmaddr;
+                stop = true;
+            }
         }
     });
     return textVmAddr;
@@ -2370,25 +1429,10 @@
     return result;
 }
 
-bool Header::hasDiscontiguousSegments() const
-{
-    uint64_t loadAddr = preferredLoadAddress();
-    __block bool res = false;
-    this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        if ( info.maxProt == 0 )
-            return;
-        if ( info.vmaddr < loadAddr ) {
-            res = true;
-            stop = true;
-        }
-    });
-    return res;
-}
-
 std::string_view Header::segmentName(uint32_t segIndex) const
 {
-    __block std::string_view    result;
-    __block uint32_t            segCount = 0;
+    __block std::string_view result;
+    __block uint32_t    segCount = 0;
     this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
         if ( segIndex == segCount ) {
             result = info.segmentName;
@@ -2399,236 +1443,65 @@
     return result;
 }
 
-uint64_t Header::segmentVmSize(uint32_t segIndex) const
-{
-    __block uint64_t  result   = 0;
-    __block uint32_t  segCount = 0;
-    this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        if ( segIndex == segCount ) {
-            result = info.vmsize;
-            stop   = true;
-        }
-        ++segCount;
-    });
-    return result;
-}
-
-uint64_t Header::segmentVmAddr(uint32_t segIndex) const
-{
-    __block uint64_t  result   = 0;
-    __block uint32_t  segCount = 0;
-    this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        if ( segIndex == segCount ) {
-            result = info.vmaddr;
-            stop   = true;
-        }
-        ++segCount;
-    });
-    return result;
-}
-
-uint32_t Header::segmentFileOffset(uint32_t segIndex) const
-{
-    __block uint32_t  result   = 0;
-    __block uint32_t  segCount = 0;
-    this->forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        if ( segIndex == segCount ) {
-            result = info.fileOffset;
-            stop   = true;
-        }
-        ++segCount;
-    });
-    return result;
-}
-
-// LC_SEGMENT stores names as char[16] potentially without a null terminator.
-std::string_view Header::name16(const char name[16])
+// LC_SEGMENT stores names as char[16] potentially without a null terminator.  This returns a string_view for the given name
+static std::string_view name16(const char name[16])
 {
     size_t length = strnlen(name, 16);
-    if ( length < 16 )
-        return std::string_view(name, length);
-
-    return std::string_view(name, 16);
+    return std::string_view(name, length);
 }
 
 void Header::forEachSegment(void (^callback)(const SegmentInfo& infos, bool& stop)) const
 {
-    __block uint16_t segmentIndex = 0;
     forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
         if ( cmd->cmd == LC_SEGMENT_64 ) {
             const segment_command_64* segCmd = (segment_command_64*)cmd;
-            SegmentInfo segInfo {
-                .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
-                .fileOffset=(uint32_t)segCmd->fileoff, .fileSize=(uint32_t)segCmd->filesize, .flags=segCmd->flags,
-                .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
-            };
+            SegmentInfo segInfo { .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
+                .fileOffset=(uint32_t)segCmd->fileoff, .fileSize=(uint32_t)segCmd->filesize, .flags=segCmd->flags, .perms=(uint8_t)segCmd->initprot };
             callback(segInfo, stop);
-            ++segmentIndex;
         }
         else if ( cmd->cmd == LC_SEGMENT ) {
             const segment_command* segCmd = (segment_command*)cmd;
-            SegmentInfo segInfo {
-                .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
-                .fileOffset=segCmd->fileoff, .fileSize=segCmd->filesize, .flags=segCmd->flags,
-                .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
-            };
+            SegmentInfo segInfo { .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
+                .fileOffset=segCmd->fileoff, .fileSize=segCmd->filesize, .flags=segCmd->flags, .perms=(uint8_t)segCmd->initprot };
             callback(segInfo, stop);
-            ++segmentIndex;
-        }
-    });
-}
-
-void Header::forEachSegment(void (^callback)(const SegmentInfo& infos, uint64_t sizeOfSections, uint32_t maxAlignOfSections, bool& stop)) const
-{
-    __block uint16_t segmentIndex = 0;
-    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_SEGMENT_64 ) {
-            const segment_command_64* segCmd = (segment_command_64*)cmd;
-            uint64_t sizeOfSections = segCmd->vmsize;
-            uint32_t maxAlignOfSections = 0;
-            const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
-            const section_64* const sectionsEnd   = &sectionsStart[segCmd->nsects];
-            for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
-                if ( sect->align > maxAlignOfSections )
-                    maxAlignOfSections = sect->align;
-            }
-            SegmentInfo segInfo {
-                .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
-                .fileOffset=(uint32_t)segCmd->fileoff, .fileSize=(uint32_t)segCmd->filesize, .flags=segCmd->flags,
-                .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
-            };
-            callback(segInfo, sizeOfSections, maxAlignOfSections, stop);
-            ++segmentIndex;
-        }
-        else if ( cmd->cmd == LC_SEGMENT ) {
-            const segment_command* segCmd = (segment_command*)cmd;
-            uint64_t sizeOfSections = segCmd->vmsize;
-            uint32_t maxAlignOfSections = 0;
-            const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
-            const section* const sectionsEnd   = &sectionsStart[segCmd->nsects];
-            for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                sizeOfSections = sect->addr + sect->size - segCmd->vmaddr;
-                if ( sect->align > maxAlignOfSections )
-                    maxAlignOfSections = sect->align;
-            }
-            SegmentInfo segInfo {
-                .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
-                .fileOffset=segCmd->fileoff, .fileSize=segCmd->filesize, .flags=segCmd->flags,
-                .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
-            };
-            callback(segInfo, sizeOfSections, maxAlignOfSections, stop);
-            ++segmentIndex;
         }
     });
 }
 
 void Header::forEachSection(void (^callback)(const SectionInfo&, bool& stop)) const
 {
-    __block uint16_t segmentIndex = 0;
+    __block uint64_t prefLoadAddr = 0;
     forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
         if ( cmd->cmd == LC_SEGMENT_64 ) {
             const segment_command_64* segCmd        = (segment_command_64*)cmd;
+            if ( strcmp(segCmd->segname, "__TEXT") == 0 )
+                prefLoadAddr = segCmd->vmaddr;
             const section_64* const   sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
             const section_64* const   sectionsEnd   = &sectionsStart[segCmd->nsects];
-            std::string_view segName = name16(segCmd->segname);
+
             for ( const section_64* sect = sectionsStart; !stop && (sect < sectionsEnd); ++sect ) {
-                std::string_view segNameFromSectLC = name16(sect->segname);
-                SectionInfo info = {
-                    // Note: use of segCmd->segname is for bin compat for copy protection in rdar://146096183
-                    .sectionName=name16(sect->sectname), .segmentName=segNameFromSectLC.empty() ? segName : segNameFromSectLC, .segIndex=segmentIndex,
-                    .segMaxProt=(uint32_t)segCmd->maxprot, .segInitProt=(uint32_t)segCmd->initprot,
-                    .flags=sect->flags, .alignment=sect->align, .address=sect->addr, .size=sect->size, .fileOffset=sect->offset,
-                    .relocsOffset=sect->reloff, .relocsCount=sect->nreloc, .reserved1=sect->reserved1, .reserved2=sect->reserved2
-                };
+                std::string_view sectName = name16(sect->sectname);
+                std::string_view segName = name16(sect->segname);
+                SectionInfo info = { segName, sectName, (uint32_t)segCmd->initprot, sect->flags, sect->align, sect->addr - prefLoadAddr, sect->size, sect->offset,
+                                     sect->reloff, sect->nreloc, sect->reserved1, sect->reserved2};
                 callback(info, stop);
             }
-            ++segmentIndex;
         }
         else if ( cmd->cmd == LC_SEGMENT ) {
             const segment_command* segCmd        = (segment_command*)cmd;
+            if ( strcmp(segCmd->segname, "__TEXT") == 0 )
+                prefLoadAddr = segCmd->vmaddr;
             const section* const   sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
             const section* const   sectionsEnd   = &sectionsStart[segCmd->nsects];
             for ( const section* sect = sectionsStart; !stop && (sect < sectionsEnd); ++sect ) {
-                SectionInfo info = {
-                    .sectionName=name16(sect->sectname), .segmentName=name16(sect->segname), .segIndex=segmentIndex,
-                    .segMaxProt=(uint32_t)segCmd->maxprot, .segInitProt=(uint32_t)segCmd->initprot,
-                    .flags=sect->flags, .alignment=sect->align, .address=sect->addr, .size=sect->size, .fileOffset=sect->offset,
-                    .relocsOffset=sect->reloff, .relocsCount=sect->nreloc, .reserved1=sect->reserved1, .reserved2=sect->reserved2
-                };
+                std::string_view sectName = name16(sect->sectname);
+                std::string_view segName = name16(sect->segname);
+                SectionInfo info = { segName, sectName, (uint32_t)segCmd->initprot, sect->flags, sect->align, sect->addr - prefLoadAddr, sect->size, sect->offset,
+                                     sect->reloff, sect->nreloc, sect->reserved1, sect->reserved2};
                 callback(info, stop);
             }
-            ++segmentIndex;
-        }
-    });
-}
-
-void Header::forEachSection(void (^callback)(const SegmentInfo&, const SectionInfo&, bool& stop)) const
-{
-    __block uint16_t segmentIndex = 0;
-    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
-        if ( cmd->cmd == LC_SEGMENT_64 ) {
-            const segment_command_64*   segCmd          = (segment_command_64*)cmd;
-            const section_64* const     sectionsStart   = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
-            const section_64* const     sectionsEnd     = &sectionsStart[segCmd->nsects];
-            SegmentInfo segInfo {
-                .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
-                .fileOffset=(uint32_t)segCmd->fileoff, .fileSize=(uint32_t)segCmd->filesize, .flags=segCmd->flags,
-                .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
-            };
-            for ( const section_64* sect = sectionsStart; !stop && (sect < sectionsEnd); ++sect ) {
-                SectionInfo info = {
-                    .sectionName=name16(sect->sectname), .segmentName=name16(sect->segname), .segIndex=segmentIndex,
-                    .segMaxProt=(uint32_t)segCmd->maxprot, .segInitProt=(uint32_t)segCmd->initprot,
-                    .flags=sect->flags, .alignment=sect->align, .address=sect->addr, .size=sect->size, .fileOffset=sect->offset,
-                    .relocsOffset=sect->reloff, .relocsCount=sect->nreloc, .reserved1=sect->reserved1, .reserved2=sect->reserved2
-                };
-                callback(segInfo, info, stop);
-            }
-            ++segmentIndex;
-        }
-        else if ( cmd->cmd == LC_SEGMENT ) {
-            const segment_command*  segCmd          = (segment_command*)cmd;
-            const section* const    sectionsStart   = (section*)((char*)segCmd + sizeof(struct segment_command));
-            const section* const    sectionsEnd     = &sectionsStart[segCmd->nsects];
-            SegmentInfo segInfo {
-                .segmentName=name16(segCmd->segname), .vmaddr=segCmd->vmaddr, .vmsize=segCmd->vmsize,
-                .fileOffset=segCmd->fileoff, .fileSize=segCmd->filesize, .flags=segCmd->flags,
-                .segmentIndex=segmentIndex, .maxProt=(uint8_t)segCmd->maxprot, .initProt=(uint8_t)segCmd->initprot,
-            };
-            for ( const section* sect = sectionsStart; !stop && (sect < sectionsEnd); ++sect ) {
-                SectionInfo info = {
-                    .sectionName=name16(sect->sectname), .segmentName=name16(sect->segname), .segIndex=segmentIndex,
-                    .segMaxProt=(uint32_t)segCmd->maxprot, .segInitProt=(uint32_t)segCmd->initprot,
-                    .flags=sect->flags, .alignment=sect->align, .address=sect->addr, .size=sect->size, .fileOffset=sect->offset,
-                    .relocsOffset=sect->reloff, .relocsCount=sect->nreloc, .reserved1=sect->reserved1, .reserved2=sect->reserved2
-                };
-                callback(segInfo, info, stop);
-            }
-            ++segmentIndex;
-        }
-    });
-}
-
-std::span<const uint8_t> Header::findSectionContent(CString segName, CString sectName, bool useVmOffset) const
-{
-    __block std::span<const uint8_t> result;
-    this->forEachSection(^(const SectionInfo& info, bool& stop) {
-        if ( (info.segmentName == segName) && (info.sectionName == sectName) ) {
-            const uint8_t* sectionContent = nullptr;
-            if ( useVmOffset ) {
-                // dyld loaded image, find section based on vmaddr
-                sectionContent = (uint8_t*)this + (info.address - this->preferredLoadAddress());
-            }
-            else {
-                // file mapped image, use file offsets to get content
-                sectionContent = (uint8_t*)this + info.fileOffset;
-            }
-            stop = true;
-            result = std::span<const uint8_t>(sectionContent, (size_t)info.size);
-        }
-    });
-    return result;
+        }
+    });
 }
 
 // add any LINKEDIT content file-offset in load commands to this to get content
@@ -2653,11 +1526,13 @@
     // need to find LINKEDIT and TEXT to compute difference of file offsets vs vm offsets
     __block uint64_t result         = 0;
     __block uint64_t textVmAddr     = 0;
+    __block uint64_t textFileOffset = 0;
     forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
         if ( cmd->cmd == LC_SEGMENT_64 ) {
             const segment_command_64* segCmd = (segment_command_64*)cmd;
             if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
                 textVmAddr     = segCmd->vmaddr;
+                textFileOffset = segCmd->fileoff;
             }
             else if ( strcmp(segCmd->segname, "__LINKEDIT") == 0 ) {
                 uint64_t vmOffsetToLinkedit   = segCmd->vmaddr - textVmAddr;
@@ -2670,10 +1545,11 @@
             const segment_command* segCmd = (segment_command*)cmd;
             if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
                 textVmAddr     = segCmd->vmaddr;
+                textFileOffset = segCmd->fileoff;
             }
             else if ( strcmp(segCmd->segname, "__LINKEDIT") == 0 ) {
                 uint64_t vmOffsetToLinkedit   = segCmd->vmaddr - textVmAddr;
-                uint64_t fileOffsetToLinkedit = segCmd->fileoff;
+                uint64_t fileOffsetToLinkedit = segCmd->fileoff - textFileOffset;
                 result                        = vmOffsetToLinkedit - fileOffsetToLinkedit;
                 stop                          = true;
             }
@@ -2695,44 +1571,28 @@
     return result;
 }
 
-// checks if a section exists.
-// use `segNamePrefix` to just match segment name prefix (e.g. "__DATA" matches "__DATA_CONST")
-bool Header::hasSection(CString segName, CString sectName, bool segNamePrefix) const
+bool Header::isRestricted() const
 {
     __block bool result = false;
     this->forEachSection(^(const SectionInfo& info, bool& stop) {
-        if ( info.sectionName == sectName ) {
-            if ( (segNamePrefix && info.segmentName.starts_with(segName)) || (info.segmentName == segName)) {
-                result = true;
-                stop   = true;
-            }
+        if ( (info.segmentName == "__RESTRICT") && (info.sectionName == "__restrict") ) {
+            result = true;
+            stop   = true;
         }
     });
     return result;
 }
 
-bool Header::isRestricted() const
-{
-    return this->hasSection("__RESTRICT", "__restrict");
-}
-
 bool Header::hasInterposingTuples() const
 {
     __block bool hasInterposing = false;
-    this->forEachInterposingSection(^(const SectionInfo& info, bool& stop) {
-        hasInterposing = true;
-        stop           = true;
-    });
-    return hasInterposing;
-}
-
-void Header::forEachInterposingSection(void (^callback)(const SectionInfo&, bool& stop)) const
-{
     this->forEachSection(^(const SectionInfo& info, bool& stop) {
         if ( ((info.flags & SECTION_TYPE) == S_INTERPOSING) || ((info.sectionName == "__interpose") && (info.segmentName.starts_with("__DATA") || info.segmentName.starts_with("__AUTH"))) ) {
-            callback(info, stop);
-        }
-    });
+            hasInterposing = true;
+            stop           = true;
+        }
+    });
+    return hasInterposing;
 }
 
 bool Header::hasObjC() const
@@ -2767,6 +1627,11 @@
     return hasEncryptionInfo(cryptId, textOffset, size) && cryptId == 1;
 }
 
+bool Header::canBeFairPlayEncrypted() const
+{
+    return (findFairPlayEncryptionLoadCommand() != nullptr);
+}
+
 const encryption_info_command* Header::findFairPlayEncryptionLoadCommand() const
 {
     __block const encryption_info_command* result = nullptr;
@@ -2808,13 +1673,6 @@
         }
     });
 }
-
-bool Header::hasFunctionVariantFixups() const
-{
-    return hasLoadCommand(LC_FUNCTION_VARIANT_FIXUPS);
-}
-
-
 
 void Header::forEachLinkerOption(void (^callback)(const char* opt, bool& stop)) const
 {
@@ -2852,44 +1710,10 @@
     return result;
 }
 
-uint32_t Header::threadLoadCommandsSize(const Architecture& arch)
-{
-    uint32_t cmdSize = sizeof(thread_command);
-    if ( arch.sameCpu(Architecture::arm64) )
-        cmdSize = Header::pointerAligned(true, 16 + 34 * 8); // base size + ARM_EXCEPTION_STATE64_COUNT * 8
-    else if ( arch.sameCpu(Architecture::arm64_32) )
-        cmdSize = Header::pointerAligned(false, 16 + 34 * 8); // base size + ARM_EXCEPTION_STATE64_COUNT * 8
-    else if ( arch.sameCpu(Architecture::x86_64) )
-        cmdSize = Header::pointerAligned(true, 16 + 42 * 4); // base size + x86_THREAD_STATE64_COUNT * 4
-    else if ( arch.usesArmOrThumbInstructions() )
-        cmdSize = Header::pointerAligned(false, 16 + 17 * 4); // base size + ARM_THREAD_STATE_COUNT * 4
-    else
-        assert(0 && "unsupported arch for thread load command");
-    return cmdSize;
-}
-
-uint32_t Header::threadLoadCommandsSize() const
-{
-    return Header::threadLoadCommandsSize(arch());
-}
 
 uint32_t Header::headerAndLoadCommandsSize() const
 {
     return machHeaderSize() + mh.sizeofcmds;
-}
-
-uint32_t Header::pointerAligned(bool is64, uint32_t value)
-{
-    // mach-o requires all load command sizes to be a multiple the pointer size
-    if ( is64 )
-        return ((value + 7) & (-8));
-    else
-        return ((value + 3) & (-4));
-}
-
-uint32_t Header::pointerAligned(uint32_t value) const
-{
-    return Header::pointerAligned(is64(), value);
 }
 
 uint32_t Header::fileSize() const
@@ -2904,9 +1728,7 @@
                 stop = true;
             }
         });
-        if ( size != 0 )
-            return size;
-        return headerAndLoadCommandsSize();
+        return size;
     }
 
     // compute file size from LINKEDIT fileoffset + filesize
@@ -2929,132 +1751,864 @@
     return size;
 }
 
-struct OldThreadsStartSection
-{
-    uint32_t        padding : 31,
-                    stride8 : 1;
-    uint32_t        chain_starts[1];
-};
-
-// ld64 can't sometimes determine the size of __thread_starts accurately,
-// because these sections have to be given a size before everything is laid out,
-// and you don't know the actual size of the chains until everything is
-// laid out. In order to account for this, the linker puts trailing 0xFFFFFFFF at
-// the end of the section, that must be ignored when walking the chains. This
-// patch adjust the section size accordingly.
-static uint32_t adjustStartsCount(uint32_t startsCount, const uint32_t* starts) {
-    for ( int i = startsCount; i > 0; --i )
-    {
-        if ( starts[i - 1] == 0xFFFFFFFF )
-            startsCount--;
-        else
+//
+// MARK: --- methods that create and modify ---
+//
+
+#if BUILDING_MACHO_WRITER
+
+Header* Header::make(std::span<uint8_t> buffer, uint32_t filetype, uint32_t flags, Architecture arch, bool addImplicitTextSegment)
+{
+    const size_t minHeaderAlignment = filetype == MH_OBJECT ? 8 : getpagesize();
+    assert(((uint64_t)buffer.data() & (minHeaderAlignment - 1)) == 0);
+    assert(buffer.size() >= sizeof(mach_header_64));
+    bzero(buffer.data(), buffer.size());
+    Header& header = *(Header*)buffer.data();
+    mach_header& mh = header.mh;
+    if ( arch.isBigEndian() ) {
+        mh.magic      = arch.is64() ? MH_CIGAM_64 : MH_CIGAM;
+        mh.filetype   = OSSwapBigToHostInt32(filetype);
+        mh.ncmds      = 0;
+        mh.sizeofcmds = OSSwapBigToHostInt32(MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL);
+        mh.flags      = OSSwapBigToHostInt32(flags);
+        arch.set(mh);
+        return &header; // can only construct mach_header for big-endian
+    }
+    else {
+        mh.magic      = arch.is64() ? MH_MAGIC_64 : MH_MAGIC;
+        mh.filetype   = filetype;
+        mh.ncmds      = 0;
+        mh.sizeofcmds = 0;
+        mh.flags      = flags;
+        arch.set(mh);
+    }
+    if ( addImplicitTextSegment && (filetype != MH_OBJECT) ) {
+        SegmentInfo segInfo { .segmentName="__TEXT", .vmaddr=0, .vmsize=0x1000, .fileOffset=0, .fileSize=0x1000, .perms=(VM_PROT_READ | VM_PROT_EXECUTE) };
+        header.addSegment(segInfo, std::array { "__text" });
+    }
+
+    return &header;
+}
+
+void Header::save(char savedPath[PATH_MAX]) const
+{
+    ::strcpy(savedPath, "/tmp/mocko-XXXXXX");
+    int fd = ::mkstemp(savedPath);
+    if ( fd != -1 ) {
+        ::pwrite(fd, this, sizeof(Header), 0);
+        ::close(fd);
+    }
+}
+
+uint32_t Header::pointerAligned(uint32_t value) const
+{
+    // mach-o requires all load command sizes to be a multiple the pointer size
+    if ( is64() )
+        return ((value + 7) & (-8));
+    else
+        return ((value + 3) & (-4));
+}
+
+load_command* Header::firstLoadCommand()
+{
+    if ( mh.magic == MH_MAGIC )
+        return (load_command*)((uint8_t*)this + sizeof(mach_header));
+    else
+        return (load_command*)((uint8_t*)this + sizeof(mach_header_64));
+}
+
+// creates space for a new load command, but does not fill in its payload
+load_command* Header::appendLoadCommand(uint32_t cmd, uint32_t cmdSize)
+{
+    load_command* thisCmd = (load_command*)((uint8_t*)firstLoadCommand() + mh.sizeofcmds);
+    thisCmd->cmd          = cmd;
+    thisCmd->cmdsize      = cmdSize;
+    mh.ncmds += 1;
+    mh.sizeofcmds += cmdSize;
+
+    return thisCmd;
+}
+
+// copies a new load command from another
+void Header::appendLoadCommand(const load_command* lc)
+{
+    load_command* thisCmd = (load_command*)((uint8_t*)firstLoadCommand() + mh.sizeofcmds);
+    ::memcpy(thisCmd, lc, lc->cmdsize);
+    mh.ncmds += 1;
+    mh.sizeofcmds += lc->cmdsize;
+}
+
+void Header::addBuildVersion(Platform platform, Version32 minOS, Version32 sdk, std::span<const build_tool_version> tools)
+{
+    assert(platform != Platform::zippered && "can't add a build command for Platform::zippered, it must be split");
+    uint32_t               lcSize = (uint32_t)(sizeof(build_version_command) + tools.size() * sizeof(build_tool_version));
+    build_version_command* bv     = (build_version_command*)appendLoadCommand(LC_BUILD_VERSION, lcSize);
+    bv->platform = platform.value();
+    bv->minos    = minOS.value();
+    bv->sdk      = sdk.value();
+    bv->ntools   = (uint32_t)tools.size();
+    if ( bv->ntools != 0 )
+        memcpy((uint8_t*)bv + sizeof(build_version_command), &tools[0], tools.size() * sizeof(build_tool_version));
+}
+
+void Header::addMinVersion(Platform platform, Version32 minOS, Version32 sdk)
+{
+    version_min_command vc;
+    vc.cmdsize = sizeof(version_min_command);
+    vc.version = minOS.value();
+    vc.sdk     = sdk.value();
+    if ( platform == Platform::macOS )
+        vc.cmd = LC_VERSION_MIN_MACOSX;
+    else if ( (platform == Platform::iOS) || (platform == Platform::iOS_simulator) )
+        vc.cmd = LC_VERSION_MIN_IPHONEOS;
+    else if ( (platform == Platform::watchOS) || (platform == Platform::watchOS_simulator) )
+        vc.cmd = LC_VERSION_MIN_WATCHOS;
+    else if ( (platform == Platform::tvOS) || (platform == Platform::tvOS_simulator) )
+        vc.cmd = LC_VERSION_MIN_TVOS;
+    else
+        assert(0 && "unknown platform");
+    appendLoadCommand((load_command*)&vc);
+}
+
+void Header::setHasThreadLocalVariables()
+{
+    assert(mh.filetype != MH_OBJECT);
+    mh.flags |= MH_HAS_TLV_DESCRIPTORS;
+}
+
+void Header::setHasWeakDefs()
+{
+    assert(mh.filetype != MH_OBJECT);
+    mh.flags |= MH_WEAK_DEFINES;
+}
+
+void Header::setUsesWeakDefs()
+{
+    assert(mh.filetype != MH_OBJECT);
+    mh.flags |= MH_BINDS_TO_WEAK;
+}
+
+void Header::setAppExtensionSafe()
+{
+    assert(mh.filetype == MH_DYLIB);
+    mh.flags |= MH_APP_EXTENSION_SAFE;
+}
+
+void Header::setSimSupport()
+{
+    assert(mh.filetype == MH_DYLIB);
+    mh.flags |= MH_SIM_SUPPORT;
+}
+
+void Header::setNoReExportedDylibs()
+{
+    assert(mh.filetype == MH_DYLIB);
+    mh.flags |= MH_NO_REEXPORTED_DYLIBS;
+}
+
+void Header::addPlatformInfo(Platform platform, Version32 minOS, Version32 sdk, std::span<const build_tool_version> tools)
+{
+    Architecture arch(&mh);
+    Policy policy(arch, { platform, minOS, sdk }, mh.filetype);
+    switch ( policy.useBuildVersionLoadCommand() ) {
+        case Policy::preferUse:
+        case Policy::mustUse:
+            // three macOS dylibs under libSystem need to be built with old load commands to support old simulator runtimes
+            if ( isSimSupport() && (platform == Platform::macOS) && ((arch == Architecture::x86_64) || (arch == Architecture::i386)) )
+                addMinVersion(platform, minOS, sdk);
+            else
+                addBuildVersion(platform, minOS, sdk, tools);
             break;
-    }
-    return startsCount;
-}
-
-bool Header::hasFirmwareChainStarts(uint16_t* pointerFormat, uint32_t* startsCount, const uint32_t** starts) const
-{
-    if ( !this->isPreload() && !this->isStaticExecutable() )
-        return false;
-
-    __block bool result = false;
-    this->forEachSection(^(const SectionInfo& info, bool& stop) {
-        if ( (info.sectionName == "__chain_starts") && (info.segmentName == "__TEXT") ) {
-            const dyld_chained_starts_offsets* startsSect = (dyld_chained_starts_offsets*)((uint8_t*)this + info.fileOffset);
-            if ( pointerFormat != nullptr )
-                *pointerFormat = startsSect->pointer_format;
-            if ( startsCount != nullptr )
-                *startsCount   = (info.size >= 4) ? startsSect->starts_count : 0;
-            if ( starts != nullptr )
-                *starts        = &startsSect->chain_starts[0];
-            result             = true;
-            stop               = true;
-        }
-        else if ( (info.sectionName == "__thread_starts") && (info.segmentName == "__TEXT") ) {
-            const OldThreadsStartSection* sect = (OldThreadsStartSection*)((uint8_t*)this + info.fileOffset);
-            if ( pointerFormat != nullptr )
-                *pointerFormat = sect->stride8 ? DYLD_CHAINED_PTR_ARM64E : DYLD_CHAINED_PTR_ARM64E_FIRMWARE;
-            if ( startsCount != nullptr )
-                *startsCount   = adjustStartsCount((uint32_t)(info.size/4) - 1, sect->chain_starts);
-            if ( starts != nullptr )
-                *starts        = sect->chain_starts;
-            result             = true;
-            stop               = true;
-        }
-    });
-    return result;
-}
-
-bool Header::hasFirmwareRebaseRuns() const
-{
-    return forEachFirmwareRebaseRuns(nullptr);
-}
-
-// content of __TEXT,__rebase_info section
-struct RebaseInfo
-{
-    uint32_t  startAddress;
-    uint8_t   runs[];   // value of even indexes is how many pointers in a row are rebases, value of odd indexes times 4 is memory to skip over
-                        // two zero values in a row signals the end of the run
-};
-
-
-bool Header::forEachFirmwareRebaseRuns(void (^handler)(uint32_t offset, bool& stop)) const
-{
-    if ( !this->isPreload() && !this->isStaticExecutable() )
-        return false;
-
-    __block const RebaseInfo* rr  = nullptr;
-    __block const RebaseInfo* end = nullptr;
-    this->forEachSection(^(const SectionInfo& info, bool& sectStop) {
-        if ( (info.sectionName == "__rebase_info") && (info.segmentName == "__TEXT") ) {
-            sectStop = true;
-            rr       = (RebaseInfo*)((uint8_t*)this + info.fileOffset);
-            end      = (RebaseInfo*)((uint8_t*)rr + info.size);
-        }
-    });
-    if ( rr == nullptr )
-        return false;
-
-    if ( handler != nullptr ) {
-        bool stop = false;
-        while ( (rr < end) && !stop ) {
-            uint32_t address = rr->startAddress;
-            int      index   = 0;
-            bool     done    = false;
-            while ( !done && !stop ) {
-                uint8_t count = rr->runs[index];
-                if ( count == 0 ) {
-                    // two 0x00 in a row mean the run is complete
-                    if ( rr->runs[index+1] == 0 ) {
-                        ++index;
-                        done = true;
+        case Policy::preferDontUse:
+        case Policy::mustNotUse:
+            addMinVersion(platform, minOS, sdk);
+            break;
+    }
+}
+
+void Header::addNullUUID()
+{
+    uuid_command uc;
+    uc.cmd     = LC_UUID;
+    uc.cmdsize = sizeof(uuid_command);
+    bzero(uc.uuid, 16);
+    appendLoadCommand((load_command*)&uc);
+}
+
+void Header::addUniqueUUID(uuid_t copyOfUUID)
+{
+    uuid_command uc;
+    uc.cmd     = LC_UUID;
+    uc.cmdsize = sizeof(uuid_command);
+    uuid_generate_random(uc.uuid);
+    appendLoadCommand((load_command*)&uc);
+    if ( copyOfUUID )
+        memcpy(copyOfUUID, uc.uuid, sizeof(uuid_t));
+}
+
+void Header::updateUUID(uuid_t uuid)
+{
+    __block bool found = false;
+    forEachLoadCommandSafe(^(const load_command *cmd, bool &stop) {
+        if ( cmd->cmd == LC_UUID ) {
+            memcpy(((uuid_command*)cmd)->uuid, uuid, 16);
+            found = true;
+            stop = true;
+        }
+    });
+    assert(found && "updateUUID called without a LC_UUID command");
+}
+
+void Header::addSegment(const SegmentInfo& info, std::span<const char* const> sectionNames)
+{
+    if ( is64() ) {
+        uint32_t            lcSize = (uint32_t)(sizeof(segment_command_64) + sectionNames.size() * sizeof(section_64));
+        segment_command_64* sc     = (segment_command_64*)appendLoadCommand(LC_SEGMENT_64, lcSize);
+        strncpy(sc->segname, info.segmentName.data(), 16);
+        sc->vmaddr             = info.vmaddr;
+        sc->vmsize             = info.vmsize;
+        sc->fileoff            = info.fileOffset;
+        sc->filesize           = info.fileSize;
+        sc->initprot           = info.perms;
+        sc->maxprot            = info.perms;
+        sc->nsects             = (uint32_t)sectionNames.size();
+        sc->flags              = info.flags;
+        section_64* const sect = (section_64*)((uint8_t*)sc + sizeof(struct segment_command_64));
+        uint32_t sectionIndex  = 0;
+        for ( const char* sectName : sectionNames ) {
+            strncpy(sect[sectionIndex].segname, info.segmentName.data(), 16);
+            strncpy(sect[sectionIndex].sectname, sectName, 16);
+            ++sectionIndex;
+        }
+    }
+    else {
+        uint32_t         lcSize = (uint32_t)(sizeof(segment_command) + sectionNames.size() * sizeof(section));
+        segment_command* sc     = (segment_command*)appendLoadCommand(LC_SEGMENT, lcSize);
+        strncpy(sc->segname, info.segmentName.data(), 16);
+        sc->vmaddr             = (uint32_t)info.vmaddr;
+        sc->vmsize             = (uint32_t)info.vmsize;
+        sc->fileoff            = info.fileOffset;
+        sc->filesize           = info.fileSize;
+        sc->initprot           = info.perms;
+        sc->maxprot            = info.perms;
+        sc->nsects             = (uint32_t)sectionNames.size();
+        sc->flags              = info.flags;
+        section* const sect = (section*)((uint8_t*)sc + sizeof(struct segment_command));
+        uint32_t sectionIndex  = 0;
+        for ( const char* sectName : sectionNames ) {
+            strncpy(sect[sectionIndex].segname, info.segmentName.data(), 16);
+            strncpy(sect[sectionIndex].sectname, sectName, 16);
+            ++sectionIndex;
+        }
+    }
+}
+
+void Header::updateSection(const SectionInfo& info)
+{
+    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            segment_command_64* segCmd = (segment_command_64*)cmd;
+            if (info.segmentName == segCmd->segname) {
+                section_64* const  sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64));
+                section_64* const  sectionsEnd   = &sectionsStart[segCmd->nsects];
+                for ( section_64* sect=sectionsStart; sect < sectionsEnd; ++sect ) {
+                    if ( strncmp(info.sectionName.data(), sect->sectname, 16) == 0 ) {
+                        sect->addr      = info.address;
+                        sect->size      = info.size;
+                        sect->offset    = info.fileOffset;
+                        sect->align     = info.alignment;
+                        sect->reloff    = info.relocsOffset;
+                        sect->nreloc    = info.relocsCount;
+                        sect->flags     = info.flags;
+                        sect->reserved1 = info.reserved1;
+                        sect->reserved2 = info.reserved2;
+                        sect->reserved3 = 0;
+                        stop = true;
+                        return;
                     }
                 }
-                else {
-                    if ( index & 1 ) {
-                        // odd runs index => how much to jump forward
-                        address += ((count-1) * 4);
-                    }
-                    else {
-                        // even runs index => how many pointers in a row that need rebasing
-                        for (int i=0; i < count; ++i) {
-                            handler(address, stop);
-                            address += 4;
-                        }
+            }
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            segment_command* segCmd = (segment_command*)cmd;
+            if (info.segmentName == segCmd->segname) {
+                section* const  sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command));
+                section* const  sectionsEnd   = &sectionsStart[segCmd->nsects];
+                for ( section* sect=sectionsStart; sect < sectionsEnd; ++sect ) {
+                    if ( strncmp(info.sectionName.data(), sect->sectname, 16) == 0 ) {
+                        sect->addr      = (uint32_t)info.address;
+                        sect->size      = (uint32_t)info.size;
+                        sect->offset    = info.fileOffset;
+                        sect->align     = info.alignment;
+                        sect->reloff    = info.relocsOffset;
+                        sect->nreloc    = info.relocsCount;
+                        sect->flags     = info.flags;
+                        sect->reserved1 = info.reserved1;
+                        sect->reserved2 = info.reserved2;
+                        stop = true;
+                        return;
                     }
                 }
-                ++index;
-            }
-            // 4-byte align for next run
-            index = (index+3) & (-4);
-            rr  = (RebaseInfo*)(&rr->runs[index]);
-        }
-    }
-
-    return true;
-}
+            }
+        }
+    });
+}
+
+void Header::updateSegment(const SegmentInfo& info)
+{
+    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            segment_command_64* segCmd = (segment_command_64*)cmd;
+            if (info.segmentName == segCmd->segname) {
+                segCmd->vmaddr             = info.vmaddr;
+                segCmd->vmsize             = info.vmsize;
+                segCmd->fileoff            = info.fileOffset;
+                segCmd->filesize           = info.fileSize;
+                segCmd->initprot           = info.perms;
+                segCmd->maxprot            = info.perms;
+                stop = true;
+                return;
+            }
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            segment_command* segCmd = (segment_command*)cmd;
+            if (info.segmentName == segCmd->segname) {
+                segCmd->vmaddr             = (uint32_t)info.vmaddr;
+                segCmd->vmsize             = (uint32_t)info.vmsize;
+                segCmd->fileoff            = info.fileOffset;
+                segCmd->filesize           = info.fileSize;
+                segCmd->initprot           = info.perms;
+                segCmd->maxprot            = info.perms;
+                stop = true;
+                return;
+            }
+        }
+    });
+}
+
+
+void Header::addInstallName(const char* name, Version32 compatVers, Version32 currentVersion)
+{
+    uint32_t       alignedSize      = pointerAligned((uint32_t)(sizeof(dylib_command) + strlen(name) + 1));
+    dylib_command* ic               = (dylib_command*)appendLoadCommand(LC_ID_DYLIB, alignedSize);
+    ic->dylib.name.offset           = sizeof(dylib_command);
+    ic->dylib.current_version       = currentVersion.value();
+    ic->dylib.compatibility_version = compatVers.value();
+    strcpy((char*)ic + ic->dylib.name.offset, name);
+}
+
+void Header::addDependentDylib(const char* path, DependentDylibAttributes depAttrs, Version32 compatVers, Version32 currentVersion)
+{
+    uint32_t cmd = 0;
+    if (      depAttrs == DependentDylibAttributes::regular )
+        cmd = LC_LOAD_DYLIB;
+    else if ( depAttrs == DependentDylibAttributes::justWeakLink )
+        cmd = LC_LOAD_WEAK_DYLIB;
+    else if ( depAttrs == DependentDylibAttributes::justUpward )
+        cmd = LC_LOAD_UPWARD_DYLIB;
+    else if ( depAttrs == DependentDylibAttributes::justReExport )
+        cmd = LC_REEXPORT_DYLIB;
+
+    if ( cmd ) {
+        // make traditional load command
+        uint32_t       alignedSize = pointerAligned((uint32_t)(sizeof(dylib_command) + strlen(path) + 1));
+        dylib_command* dc          = (dylib_command*)appendLoadCommand(cmd, alignedSize);
+        dc->dylib.name.offset           = sizeof(dylib_command);
+        dc->dylib.current_version       = currentVersion.value();
+        dc->dylib.compatibility_version = compatVers.value();
+        dc->dylib.timestamp             = 2; // needs to be some constant value that is different than dylib id load command;
+        strcpy((char*)dc + dc->dylib.name.offset, path);
+    }
+    else {
+        // make new style load command with extra flags field
+        if ( depAttrs.weakLink )
+            cmd = LC_LOAD_WEAK_DYLIB;
+        else
+            cmd = LC_LOAD_DYLIB;
+        uint32_t        alignedSize = pointerAligned((uint32_t)(sizeof(dylib_use_command) + strlen(path) + 1));
+        dylib_use_command* dc          = (dylib_use_command*)appendLoadCommand(cmd, alignedSize);
+        dc->nameoff               = sizeof(dylib_use_command);
+        dc->current_version       = currentVersion.value();
+        dc->compat_version        = 0x00010000; // unused, but looks like 1.0 to old tools
+        dc->marker                = 0x1a741800; // magic value that means dylib_use_command
+        dc->flags                 = depAttrs.raw;
+        strcpy((char*)dc + dc->nameoff, path);
+    }
+}
+
+void Header::addLibSystem()
+{
+    addDependentDylib("/usr/lib/libSystem.B.dylib");
+}
+
+void Header::addDylibId(CString name, Version32 compatVers, Version32 currentVersion)
+{
+    uint32_t       alignedSize          = pointerAligned((uint32_t)(sizeof(dylib_command) + name.size() + 1));
+    dylib_command* dc                   = (dylib_command*)appendLoadCommand(LC_ID_DYLIB, alignedSize);
+    dc->dylib.name.offset               = sizeof(dylib_command);
+    dc->dylib.timestamp                 = 1; // needs to be some constant value that is different than dependent dylib
+    dc->dylib.current_version           = currentVersion.value();
+    dc->dylib.compatibility_version     = compatVers.value();
+    strcpy((char*)dc + dc->dylib.name.offset, name.c_str());
+}
+
+void Header::addDyldID()
+{
+    const char* path = "/usr/lib/dyld";
+    uint32_t       alignedSize = pointerAligned((uint32_t)(sizeof(dylinker_command) + strlen(path) + 1));
+    dylinker_command* dc       = (dylinker_command*)appendLoadCommand(LC_ID_DYLINKER, alignedSize);
+    dc->name.offset = sizeof(dylinker_command);
+    strcpy((char*)dc + dc->name.offset, path);
+}
+
+void Header::addDynamicLinker()
+{
+    const char* path = "/usr/lib/dyld";
+    uint32_t       alignedSize = pointerAligned((uint32_t)(sizeof(dylinker_command) + strlen(path) + 1));
+    dylinker_command* dc       = (dylinker_command*)appendLoadCommand(LC_LOAD_DYLINKER, alignedSize);
+    dc->name.offset = sizeof(dylinker_command);
+    strcpy((char*)dc + dc->name.offset, path);
+}
+
+void Header::addFairPlayEncrypted(uint32_t offset, uint32_t size)
+{
+    if ( is64() ) {
+        encryption_info_command_64 en64;
+        en64.cmd       = LC_ENCRYPTION_INFO_64;
+        en64.cmdsize   = sizeof(encryption_info_command_64);
+        en64.cryptoff  = offset;
+        en64.cryptsize = size;
+        en64.cryptid   = 0;
+        en64.pad       = 0;
+        appendLoadCommand((load_command*)&en64);
+    }
+    else {
+        encryption_info_command en32;
+        en32.cmd       = LC_ENCRYPTION_INFO;
+        en32.cmdsize   = sizeof(encryption_info_command);
+        en32.cryptoff  = offset;
+        en32.cryptsize = size;
+        en32.cryptid   = 0;
+        appendLoadCommand((load_command*)&en32);
+    }
+}
+
+void Header::addRPath(const char* path)
+{
+    uint32_t       alignedSize = pointerAligned((uint32_t)(sizeof(rpath_command) + strlen(path) + 1));
+    rpath_command* rc          = (rpath_command*)appendLoadCommand(LC_RPATH, alignedSize);
+    rc->path.offset            = sizeof(rpath_command);
+    strcpy((char*)rc + rc->path.offset, path);
+}
+
+void Header::addDyldEnvVar(const char* path)
+{
+    uint32_t          alignedSize = pointerAligned((uint32_t)(sizeof(dylinker_command) + strlen(path) + 1));
+    dylinker_command* dc          = (dylinker_command*)appendLoadCommand(LC_DYLD_ENVIRONMENT, alignedSize);
+    dc->name.offset               = sizeof(dylinker_command);
+    strcpy((char*)dc + dc->name.offset, path);
+}
+
+void Header::addAllowableClient(const char* clientName)
+{
+    uint32_t            alignedSize = pointerAligned((uint32_t)(sizeof(sub_client_command) + strlen(clientName) + 1));
+    sub_client_command* ac          = (sub_client_command*)appendLoadCommand(LC_SUB_CLIENT, alignedSize);
+    ac->client.offset               = sizeof(sub_client_command);
+    strcpy((char*)ac + ac->client.offset, clientName);
+}
+
+void Header::addUmbrellaName(const char* umbrellaName)
+{
+    uint32_t            alignedSize = pointerAligned((uint32_t)(sizeof(sub_framework_command) + strlen(umbrellaName) + 1));
+    sub_framework_command* ac       = (sub_framework_command*)appendLoadCommand(LC_SUB_FRAMEWORK, alignedSize);
+    ac->umbrella.offset             = sizeof(sub_framework_command);
+    strcpy((char*)ac + ac->umbrella.offset, umbrellaName);
+}
+
+void Header::addSourceVersion(Version64 vers)
+{
+    source_version_command svc;
+    svc.cmd       = LC_SOURCE_VERSION;
+    svc.cmdsize   = sizeof(source_version_command);
+    svc.version   = vers.value();
+    appendLoadCommand((load_command*)&svc);
+}
+
+void Header::setMain(uint32_t offset)
+{
+    entry_point_command ec;
+    ec.cmd       = LC_MAIN;
+    ec.cmdsize   = sizeof(entry_point_command);
+    ec.entryoff  = offset;
+    ec.stacksize = 0;
+    appendLoadCommand((load_command*)&ec);
+}
+
+void Header::setCustomStackSize(uint64_t stackSize) {
+    __block bool found = false;
+    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+        if (cmd->cmd == LC_MAIN) {
+            entry_point_command* ec = (entry_point_command*)cmd;
+            ec->stacksize           = stackSize;
+            found                   = true;
+            stop                    = true;
+        }
+    });
+    assert(found);
+}
+
+void Header::setUnixEntry(uint64_t startAddr)
+{
+    // FIXME: support other archs
+    if ( (mh.cputype == CPU_TYPE_ARM64) || (mh.cputype == CPU_TYPE_ARM64_32) ) {
+        uint32_t   lcSize = 288;
+        uint32_t*  words     = (uint32_t*)appendLoadCommand(LC_UNIXTHREAD, lcSize);
+        words[2] = 6;   // flavor = ARM_THREAD_STATE64
+        words[3] = 68;  // count  = ARM_EXCEPTION_STATE64_COUNT
+        bzero(&words[4], lcSize-16);
+        *(uint64_t*)(&words[68]) = startAddr;  // register pc = startAddr
+    }
+    else if ( mh.cputype == CPU_TYPE_X86_64 ) {
+        uint32_t   lcSize = 184;
+        uint32_t*  words     = (uint32_t*)appendLoadCommand(LC_UNIXTHREAD, lcSize);
+        words[2] = 4;   // flavor = x86_THREAD_STATE64
+        words[3] = 42;  // count  = x86_THREAD_STATE64_COUNT
+        bzero(&words[4], lcSize-16);
+        *(uint64_t*)(&words[36]) = startAddr;  // register pc = startAddr
+    }
+    else {
+        assert(0 && "arch not supported");
+    }
+}
+
+void Header::addCodeSignature(uint32_t fileOffset, uint32_t fileSize)
+{
+    linkedit_data_command lc;
+    lc.cmd       = LC_CODE_SIGNATURE;
+    lc.cmdsize   = sizeof(linkedit_data_command);
+    lc.dataoff   = fileOffset;
+    lc.datasize  = fileSize;
+    appendLoadCommand((load_command*)&lc);
+}
+
+void Header::setBindOpcodesInfo(uint32_t rebaseOffset, uint32_t rebaseSize,
+                                uint32_t bindsOffset, uint32_t bindsSize,
+                                uint32_t weakBindsOffset, uint32_t weakBindsSize,
+                                uint32_t lazyBindsOffset, uint32_t lazyBindsSize,
+                                uint32_t exportTrieOffset, uint32_t exportTrieSize)
+{
+    dyld_info_command lc;
+    lc.cmd              = LC_DYLD_INFO_ONLY;
+    lc.cmdsize          = sizeof(dyld_info_command);
+    lc.rebase_off       = rebaseOffset;
+    lc.rebase_size      = rebaseSize;
+    lc.bind_off         = bindsOffset;
+    lc.bind_size        = bindsSize;
+    lc.weak_bind_off    = weakBindsOffset;
+    lc.weak_bind_size   = weakBindsSize;
+    lc.lazy_bind_off    = lazyBindsOffset;
+    lc.lazy_bind_size   = lazyBindsSize;
+    lc.export_off       = exportTrieOffset;
+    lc.export_size      = exportTrieSize;
+    appendLoadCommand((load_command*)&lc);
+}
+
+void Header::setChainedFixupsInfo(uint32_t cfOffset, uint32_t cfSize)
+{
+    linkedit_data_command lc;
+    lc.cmd       = LC_DYLD_CHAINED_FIXUPS;
+    lc.cmdsize   = sizeof(linkedit_data_command);
+    lc.dataoff   = cfOffset;
+    lc.datasize  = cfSize;
+    appendLoadCommand((load_command*)&lc);
+}
+
+void Header::setExportTrieInfo(uint32_t offset, uint32_t size)
+{
+    linkedit_data_command lc;
+    lc.cmd       = LC_DYLD_EXPORTS_TRIE;
+    lc.cmdsize   = sizeof(linkedit_data_command);
+    lc.dataoff   = offset;
+    lc.datasize  = size;
+    appendLoadCommand((load_command*)&lc);
+}
+
+void Header::setSplitSegInfo(uint32_t offset, uint32_t size)
+{
+    linkedit_data_command lc;
+    lc.cmd       = LC_SEGMENT_SPLIT_INFO;
+    lc.cmdsize   = sizeof(linkedit_data_command);
+    lc.dataoff   = offset;
+    lc.datasize  = size;
+    appendLoadCommand((load_command*)&lc);
+}
+
+void Header::setDataInCode(uint32_t offset, uint32_t size)
+{
+    linkedit_data_command lc;
+    lc.cmd       = LC_DATA_IN_CODE;
+    lc.cmdsize   = sizeof(linkedit_data_command);
+    lc.dataoff   = offset;
+    lc.datasize  = size;
+    appendLoadCommand((load_command*)&lc);
+}
+
+void Header::setFunctionStarts(uint32_t offset, uint32_t size)
+{
+    linkedit_data_command lc;
+    lc.cmd       = LC_FUNCTION_STARTS;
+    lc.cmdsize   = sizeof(linkedit_data_command);
+    lc.dataoff   = offset;
+    lc.datasize  = size;
+    appendLoadCommand((load_command*)&lc);
+}
+
+void Header::setAtomInfo(uint32_t offset, uint32_t size)
+{
+    linkedit_data_command lc;
+    lc.cmd       = LC_ATOM_INFO;
+    lc.cmdsize   = sizeof(linkedit_data_command);
+    lc.dataoff   = offset;
+    lc.datasize  = size;
+    appendLoadCommand((load_command*)&lc);
+}
+
+void Header::setSymbolTable(uint32_t nlistOffset, uint32_t nlistCount, uint32_t stringPoolOffset, uint32_t stringPoolSize,
+                            uint32_t localsCount, uint32_t globalsCount, uint32_t undefCount, uint32_t indOffset, uint32_t indCount)
+{
+    symtab_command stc;
+    stc.cmd       = LC_SYMTAB;
+    stc.cmdsize   = sizeof(symtab_command);
+    stc.symoff    = nlistOffset;
+    stc.nsyms     = nlistCount;
+    stc.stroff    = stringPoolOffset;
+    stc.strsize   = stringPoolSize;
+    appendLoadCommand((load_command*)&stc);
+
+    dysymtab_command dstc;
+    bzero(&dstc, sizeof(dstc));
+    dstc.cmd            = LC_DYSYMTAB;
+    dstc.cmdsize        = sizeof(dysymtab_command);
+    dstc.ilocalsym      = 0;
+    dstc.nlocalsym      = localsCount;
+    dstc.iextdefsym     = localsCount;
+    dstc.nextdefsym     = globalsCount;
+    dstc.iundefsym      = localsCount+globalsCount;
+    dstc.nundefsym      = undefCount;
+    dstc.indirectsymoff = indOffset;
+    dstc.nindirectsyms  = indCount;
+    appendLoadCommand((load_command*)&dstc);
+}
+
+void Header::addLinkerOption(std::span<uint8_t> buffer, uint32_t count)
+{
+    uint32_t cmdSize = pointerAligned(sizeof(linker_option_command) + (uint32_t)buffer.size());
+
+    linker_option_command* lc = (linker_option_command*)appendLoadCommand(LC_LINKER_OPTION, cmdSize);
+    lc->cmd     = LC_LINKER_OPTION;
+    lc->cmdsize = cmdSize;
+    lc->count   = count;
+    memcpy((uint8_t*)(lc + 1), buffer.data(), buffer.size());
+}
+
+Header::LinkerOption Header::LinkerOption::make(std::span<CString> opts)
+{
+    LinkerOption out;
+    out.count = (uint32_t)opts.size();
+    assert(out.count == opts.size());
+    for ( CString option : opts ) {
+        if ( option.empty() )
+            continue;
+        size_t previousSize = out.buffer.size();
+        out.buffer.resize(previousSize + option.size() + 1);
+        option.strcpy((char*)out.buffer.data() + previousSize);
+    }
+    return out;
+}
+
+load_command* Header::findLoadCommand(uint32_t cmdNum)
+{
+    __block load_command* result = nullptr;
+    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == cmdNum ) {
+            result = (load_command*)cmd;
+            stop   = true;
+        }
+    });
+    return result;
+}
+
+void Header::removeLoadCommand(void (^callback)(const load_command* cmd, bool& remove, bool& stop))
+{
+    bool                stop      = false;
+    const load_command* startCmds = nullptr;
+    if ( mh.magic == MH_MAGIC_64 )
+        startCmds = (load_command*)((char*)this + sizeof(mach_header_64));
+    else if ( mh.magic == MH_MAGIC )
+        startCmds = (load_command*)((char*)this + sizeof(mach_header));
+    else if ( hasMachOBigEndianMagic() )
+        return; // can't process big endian mach-o
+    else {
+        //const uint32_t* h = (uint32_t*)this;
+        //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return; // not a mach-o file
+    }
+    const load_command* const cmdsEnd        = (load_command*)((char*)startCmds + mh.sizeofcmds);
+    auto                      cmd            = (load_command*)startCmds;
+    const uint32_t            origNcmds      = mh.ncmds;
+    unsigned                  bytesRemaining = mh.sizeofcmds;
+    for ( uint32_t i = 0; i < origNcmds; ++i ) {
+        bool remove  = false;
+        auto nextCmd = (load_command*)((char*)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, mh.ncmds, cmd, this, cmd->cmdsize);
+            return;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, mh.ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return;
+        }
+        callback(cmd, remove, stop);
+        if ( remove ) {
+            mh.sizeofcmds -= cmd->cmdsize;
+            ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
+            mh.ncmds--;
+        }
+        else {
+            bytesRemaining -= cmd->cmdsize;
+            cmd = nextCmd;
+        }
+        if ( stop )
+            break;
+    }
+    if ( cmd )
+        ::bzero(cmd, bytesRemaining);
+}
+
+uint32_t Header::relocatableHeaderAndLoadCommandsSize(bool is64, uint32_t sectionCount, uint32_t platformsCount, std::span<const Header::LinkerOption> linkerOptions)
+{
+    uint32_t size =  0;
+    if ( is64 ) {
+        size += sizeof(mach_header_64);
+        size += sizeof(segment_command_64);
+        size += sizeof(section_64) * sectionCount;
+    }
+    else {
+        size += sizeof(mach_header);
+        size += sizeof(segment_command);
+        size += sizeof(section) * sectionCount;
+    }
+    size += sizeof(symtab_command);
+    size += sizeof(dysymtab_command);
+    size += sizeof(build_version_command) * platformsCount;
+    size += sizeof(linkedit_data_command);
+
+    for ( Header::LinkerOption opt : linkerOptions ) {
+        size += opt.lcSize();
+    }
+    return size;
+}
+
+void Header::setRelocatableSectionCount(uint32_t sectionCount)
+{
+    assert(mh.filetype == MH_OBJECT);
+    if ( is64() ) {
+        uint32_t            lcSize = (uint32_t)(sizeof(segment_command_64) + sectionCount * sizeof(section_64));
+        segment_command_64* sc = (segment_command_64*)appendLoadCommand(LC_SEGMENT_64, lcSize);
+        sc->segname[0]         = '\0';   // MH_OBJECT has one segment with no name
+        sc->vmaddr             = 0;
+        sc->vmsize             = 0;   // adjusted in updateRelocatableSegmentSize()
+        sc->fileoff            = 0;
+        sc->filesize           = 0;   // adjusted in updateRelocatableSegmentSize()
+        sc->initprot           = 7;
+        sc->maxprot            = 7;
+        sc->nsects             = sectionCount;
+        // section info to be filled in later by setRelocatableSectionInfo()
+        bzero((uint8_t*)sc + sizeof(segment_command_64), sectionCount * sizeof(section_64));
+    }
+    else {
+        uint32_t            lcSize = (uint32_t)(sizeof(segment_command) + sectionCount * sizeof(section));
+        segment_command* sc = (segment_command*)appendLoadCommand(LC_SEGMENT, lcSize);
+        sc->segname[0]         = '\0';   // MH_OBJECT has one segment with no name
+        sc->vmaddr             = 0;
+        sc->vmsize             = 0x1000; // FIXME: need dynamic segment layout
+        sc->fileoff            = 0;
+        sc->filesize           = 0x1000;
+        sc->initprot           = 7;
+        sc->maxprot            = 7;
+        sc->nsects             = sectionCount;
+        // section info to be filled in later by setRelocatableSectionInfo()
+        bzero((uint8_t*)sc + sizeof(segment_command), sectionCount * sizeof(struct section));
+    }
+}
+
+void Header::updateRelocatableSegmentSize(uint64_t vmSize, uint32_t fileSize)
+{
+    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT ) {
+            segment_command* sc = (segment_command*)cmd;
+            sc->vmsize   = (uint32_t)vmSize;
+            sc->filesize = fileSize;
+            stop = true;
+        }
+        else if ( cmd->cmd == LC_SEGMENT_64 ) {
+            segment_command_64* sc = (segment_command_64*)cmd;
+            sc->vmsize   = vmSize;
+            sc->filesize = fileSize;
+            stop = true;
+        }
+    });
+}
+
+
+void Header::setRelocatableSectionInfo(uint32_t sectionIndex, const char* segName, const char* sectName,
+                                       uint32_t flags, uint64_t address, uint64_t size, uint32_t fileOffset,
+                                       uint16_t alignment, uint32_t relocsOffset, uint32_t relocsCount)
+{
+    __block struct section*    section32 = nullptr;
+    __block struct section_64* section64 = nullptr;
+    forEachLoadCommandSafe(^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT ) {
+            struct section* sections = (struct section*)((uint8_t*)cmd + sizeof(segment_command));
+            section32 = &sections[sectionIndex];
+            stop = true;
+        }
+        else if ( cmd->cmd == LC_SEGMENT_64 ) {
+            struct section_64* sections = (struct section_64*)((uint8_t*)cmd + sizeof(segment_command_64));
+            section64 = &sections[sectionIndex];
+            stop = true;
+        }
+    });
+    if ( section64 != nullptr ) {
+        strncpy(section64->segname,  segName, 16);
+        strncpy(section64->sectname, sectName, 16);
+        section64->addr      = address;
+        section64->size      = size;
+        section64->offset    = fileOffset;
+        section64->align     = alignment;
+        section64->reloff    = relocsOffset;
+        section64->nreloc    = relocsCount;
+        section64->flags     = flags;
+        section64->reserved1 = 0;
+        section64->reserved2 = 0;
+        section64->reserved3 = 0;
+    }
+    else if ( section32 != nullptr ) {
+        strncpy(section32->segname,  segName, 16);
+        strncpy(section32->sectname, sectName, 16);
+        section32->addr      = (uint32_t)address;
+        section32->size      = (uint32_t)size;
+        section32->offset    = fileOffset;
+        section32->align     = alignment;
+        section32->reloff    = relocsOffset;
+        section32->nreloc    = relocsCount;
+        section32->flags     = flags;
+        section32->reserved1 = 0;
+        section32->reserved2 = 0;
+    }
+}
+
+#endif // BUILDING_MACHO_WRITER
 
 
 } // namespace dyld3