Loading...
dyld3/shared-cache/OptimizerLinkedit.cpp dyld-551.3 dyld-851.27
--- dyld/dyld-551.3/dyld3/shared-cache/OptimizerLinkedit.cpp
+++ dyld/dyld-851.27/dyld3/shared-cache/OptimizerLinkedit.cpp
@@ -40,7 +40,7 @@
 #include "Trie.hpp"
 #include "DyldSharedCache.h"
 #include "CacheBuilder.h"
-
+#include "MachOLoaded.h"
 
 #define ALIGN_AS_TYPE(value, type) \
         ((value + alignof(type) - 1) & (-alignof(type)))
@@ -53,27 +53,31 @@
 public:
     // add a string and symbol table entry index to be updated later
     void add(uint32_t symbolIndex, const char* symbolName) {
-        _map[symbolName].push_back(symbolIndex);
+        _map[symbolName].push_back({ symbolIndex, false });
+    }
+
+    // add a string and symbol table entry index to be updated later
+    void addIndirect(uint32_t symbolIndex, const char* symbolName) {
+        _map[symbolName].push_back({ symbolIndex, true });
     }
 
     // copy sorted strings to buffer and update all symbol's string offsets
     uint32_t copyPoolAndUpdateOffsets(char* dstStringPool, macho_nlist<P>* symbolTable) {
-        // make sorted list of strings
-        std::vector<std::string> allStrings;
-        allStrings.reserve(_map.size());
-        for (auto& entry : _map) {
-            allStrings.push_back(entry.first);
-        }
-        std::sort(allStrings.begin(), allStrings.end());
         // walk sorted list of strings
         dstStringPool[0] = '\0'; // tradition for start of pool to be empty string
         uint32_t poolOffset = 1;
-        for (const std::string& symName : allStrings) {
+        for (auto& entry : _map) {
+            const std::string& symName = entry.first;
             // append string to pool
             strcpy(&dstStringPool[poolOffset], symName.c_str());
             //  set each string offset of each symbol using it
-            for (uint32_t symbolIndex : _map[symName]) {
-                symbolTable[symbolIndex].set_n_strx(poolOffset);
+            for (std::pair<uint32_t, bool> symbolIndexAndIndirect : entry.second) {
+                if ( symbolIndexAndIndirect.second ) {
+                    // Indirect
+                    symbolTable[symbolIndexAndIndirect.first].set_n_value(poolOffset);
+                } else {
+                    symbolTable[symbolIndexAndIndirect.first].set_n_strx(poolOffset);
+                }
             }
             poolOffset += symName.size() + 1;
         }
@@ -91,10 +95,11 @@
 
 
 private:
-    std::unordered_map<std::string, std::vector<uint32_t>> _map;
+    std::map<std::string, std::vector<std::pair<uint32_t, bool>>> _map;
 };
 
 
+} // anonymous namespace
 
 
 struct LocalSymbolInfo
@@ -108,12 +113,12 @@
 template <typename P>
 class LinkeditOptimizer {
 public:
-                    LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag);
+                    LinkeditOptimizer(const void* containerBuffer, macho_header<P>* mh, const char* dylibID,
+                                      Diagnostics& diag);
 
     uint32_t        linkeditSize() { return _linkeditSize; }
-    uint32_t        linkeditOffset() { return _linkeditCacheOffset; }
     uint64_t        linkeditAddr() { return _linkeditAddr; }
-    const char*     installName() { return _installName; }
+    const char*     dylibID() { return _dylibID; }
     void            copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
     void            copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
     void            copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
@@ -130,6 +135,9 @@
                                        uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount,
                                        uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize);
 
+    typedef CacheBuilder::DylibStripMode DylibStripMode;
+    void            setStripMode(DylibStripMode stripMode);
+
     macho_header<P>*                        machHeader() { return _mh; }
     const std::vector<const char*>          getDownwardDependents() { return _downDependentPaths; }
     const std::vector<const char*>          getAllDependents() { return _allDependentPaths; }
@@ -144,22 +152,28 @@
     const std::vector<macho_segment_command<P>*>&  segCmds() { return _segCmds; }
 
 
+    static void optimizeLinkedit(CacheBuilder& builder, const void* containerBuffer,
+                                 CacheBuilder::UnmappedRegion* localSymbolsRegion,
+                                 const std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>>& images);
+    static void mergeLinkedits(CacheBuilder& builder, CacheBuilder::UnmappedRegion* localSymbolsRegion,
+                               std::vector<LinkeditOptimizer<P>*>& optimizers);
+
 private:
 
     typedef typename P::uint_t pint_t;
     typedef typename P::E E;
 
     macho_header<P>*                        _mh;
-    void*                                   _cacheBuffer;
+    const void*                             _containerBuffer;
     Diagnostics&                            _diagnostics;
     uint32_t                                _linkeditSize        = 0;
-    uint32_t                                _linkeditCacheOffset = 0;
     uint64_t                                _linkeditAddr        = 0;
     const uint8_t*                          _linkeditBias       = nullptr;
-    const char*                             _installName        = nullptr;
+    const char*                             _dylibID            = nullptr;
     macho_symtab_command<P>*                _symTabCmd          = nullptr;
     macho_dysymtab_command<P>*              _dynSymTabCmd       = nullptr;
     macho_dyld_info_command<P>*             _dyldInfo           = nullptr;
+    macho_linkedit_data_command<P>*         _exportTrieCmd      = nullptr;
     macho_linkedit_data_command<P>*         _functionStartsCmd  = nullptr;
     macho_linkedit_data_command<P>*         _dataInCodeCmd      = nullptr;
     std::vector<macho_segment_command<P>*>  _segCmds;
@@ -185,352 +199,20 @@
     uint32_t                                _newDataInCodeOffset            = 0;
     uint32_t                                _newIndirectSymbolTableOffset   = 0;
     uint64_t                                _dyldSectionAddr                = 0;
+    DylibStripMode                          _stripMode                 = DylibStripMode::stripAll;
 };
 
 
-
-template <typename P>
-class AcceleratorTables {
-public:
-                AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers);
-
-    uint32_t    totalSize() const;
-    void        copyTo(uint8_t* buffer);
-
-private:
-    typedef typename P::E  E;
-
-    struct NodeChain;
-
-    struct DepNode {
-        std::vector<DepNode*>   _dependents;
-        unsigned                _depth;
-        const char*             _installName;
-
-                                DepNode() : _depth(0), _installName(nullptr) { }
-        void                    computeDepth();
-        static void             verifyUnreachable(DepNode* target, NodeChain& chain, Diagnostics& diag, std::unordered_set<DepNode*>& visitedNodes, const std::vector<DepNode*>& from);
-    };
-
-    struct NodeChain {
-        NodeChain*   prev;
-        DepNode*     node;
-    };
-
-    std::unordered_map<macho_header<P>*, DepNode>                   _depDAG;
-    std::vector<dyld_cache_image_info_extra>                        _extraInfo;
-    std::vector<uint8_t>                                            _trieBytes;
-    std::vector<uint16_t>                                           _reExportArray;
-    std::vector<uint16_t>                                           _dependencyArray;
-    std::vector<uint16_t>                                           _bottomUpArray;
-    std::vector<dyld_cache_accelerator_initializer>                 _initializers;
-    std::vector<dyld_cache_accelerator_dof>                         _dofSections;
-    std::vector<dyld_cache_range_entry>                             _rangeTable;
-    std::unordered_map<macho_header<P>*, uint32_t>                  _machHeaderToImageIndex;
-    std::unordered_map<std::string, macho_header<P>*>               _dylibPathToMachHeader;
-    std::unordered_map<macho_header<P>*, LinkeditOptimizer<P>*>     _machHeaderToOptimizer;
-    dyld_cache_accelerator_info                                     _acceleratorInfoHeader;
-};
-
-
-template <typename P>
-void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain, Diagnostics& diag,
-                                                                        std::unordered_set<DepNode*>& visitedNodes, const std::vector<AcceleratorTables<P>::DepNode*>& from) {
-    for (DepNode* node : from) {
-        bool foundCycle = (node == target);
-        for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) {
-            if ( c->node == target ) {
-                foundCycle = true;
-                break;
-            }
-        }
-        if ( foundCycle ) {
-            NodeChain* chp = &chain;
-            std::string msg = std::string("found cycle for ") + target->_installName;
-            while (chp != nullptr) {
-                msg = msg + "\n  " + chp->node->_installName;
-                chp = chp->prev;
-            }
-            diag.warning("%s", msg.c_str());
-            return;
-        }
-
-        if ( visitedNodes.count(node) )
-            continue;
-        visitedNodes.insert(node);
-        NodeChain nextChain;
-        nextChain.prev = &chain;
-        nextChain.node = node;
-        verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents);
-    }
-}
-
-const uint16_t kBranchIslandDylibIndex = 0x7FFF;
-
-template <typename P>
-AcceleratorTables<P>::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector<LinkeditOptimizer<P>*>& optimizers)
-{
-    // build table mapping tables to map between mach_header, index, and optimizer
-    for ( LinkeditOptimizer<P>* op : optimizers ) {
-        _machHeaderToOptimizer[op->machHeader()] = op;
-    }
-    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset);
-    uint64_t cacheStartAddress = mappings[0].address;
-    const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset);
-    for (unsigned i=0; i < cache->header.imagesCount; ++i) {
-        uint64_t segCacheFileOffset = images[i].address - cacheStartAddress;
-        macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cache+segCacheFileOffset);
-        const char* path = (char*)cache + images[i].pathFileOffset;
-        _dylibPathToMachHeader[path] = mhMapped;
-        // don't add alias entries (path offset in pool near start of cache) to header->index map
-        if ( images[i].pathFileOffset > segCacheFileOffset )
-            _machHeaderToImageIndex[mhMapped] = i;
-    }
-
-
-    // build DAG of image dependencies
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        _depDAG[op->machHeader()]._installName = op->installName();
-    }
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        DepNode& node = _depDAG[op->machHeader()];
-        for (const char* depPath : op->getDownwardDependents()) {
-            macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
-            if ( depMH != nullptr ) {
-                DepNode* depNode = &_depDAG[depMH];
-                node._dependents.push_back(depNode);
-            }
-        }
-    }
-
-    // check for cycles in DAG
-    for (auto& entry : _depDAG) {
-        DepNode* node = &entry.second;
-        NodeChain chain;
-        chain.prev = nullptr;
-        chain.node = node;
-        std::unordered_set<DepNode*> visitedNodes;
-        DepNode::verifyUnreachable(node, chain, diag, visitedNodes, node->_dependents);
-    }
-
-    // compute depth for each DAG node
-    for (auto& entry : _depDAG) {
-        entry.second.computeDepth();
-    }
-
-    // build sorted (bottom up) list of images
-    std::vector<macho_header<P>*> sortedMachHeaders;
-    sortedMachHeaders.reserve(optimizers.size());
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        if ( strcmp(op->installName(), "dyld_shared_cache_branch_islands") != 0 )
-            sortedMachHeaders.push_back(op->machHeader());
-        else
-            _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex;
-    }
-    std::sort(sortedMachHeaders.begin(), sortedMachHeaders.end(),
-              [&](macho_header<P>* lmh, macho_header<P>* rmh) -> bool {
-                if ( _depDAG[lmh]._depth != _depDAG[rmh]._depth )
-                    return (_depDAG[lmh]._depth < _depDAG[rmh]._depth);
-                else
-                    return (lmh < rmh);
-              });
-
-    // build zeroed array of extra infos
-    dyld_cache_image_info_extra emptyExtra;
-    emptyExtra.exportsTrieAddr              = 0;
-    emptyExtra.weakBindingsAddr             = 0;
-    emptyExtra.exportsTrieSize              = 0;
-    emptyExtra.weakBindingsSize             = 0;
-    emptyExtra.dependentsStartArrayIndex    = 0;
-    emptyExtra.reExportsStartArrayIndex     = 0;
-    _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra);
-
-    //for ( macho_header<P>* mh : sortedMachHeaders ) {
-    //    fprintf(stderr, "depth: %3d mh: %p  path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName());
-    //}
-
-    // build dependency table
-    _dependencyArray.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies"
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned index = _machHeaderToImageIndex[mh];
-        auto depPaths = op->getAllDependents();
-        if ( depPaths.empty() ) {
-            _extraInfo[index].dependentsStartArrayIndex = 0;
-        }
-        else {
-            _extraInfo[index].dependentsStartArrayIndex = (uint32_t)_dependencyArray.size();
-            auto downPaths = op->getDownwardDependents();
-            for (const char* depPath : depPaths) {
-                macho_header<P>* depMH = _dylibPathToMachHeader[depPath];
-                uint16_t depIndex = _machHeaderToImageIndex[depMH];
-                if ( std::find(downPaths.begin(), downPaths.end(), depPath) == downPaths.end())
-                    depIndex |= 0x8000;
-                _dependencyArray.push_back(depIndex);
-            }
-            _dependencyArray.push_back(0xFFFF); // mark end of list
-       }
-    }
-
-    // build re-exports table
-    _reExportArray.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports"
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned index = _machHeaderToImageIndex[mh];
-        auto reExPaths = op->getReExportPaths();
-        if ( reExPaths.empty() ) {
-            _extraInfo[index].reExportsStartArrayIndex = 0;
-        }
-        else {
-            _extraInfo[index].reExportsStartArrayIndex = (uint32_t)_reExportArray.size();
-            for (const char* reExPath : reExPaths) {
-                macho_header<P>* reExMH = _dylibPathToMachHeader[reExPath];
-                uint32_t reExIndex = _machHeaderToImageIndex[reExMH];
-                _reExportArray.push_back(reExIndex);
-            }
-            _reExportArray.push_back(0xFFFF); // mark end of list
-       }
-    }
-
-    // build ordered list of initializers
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned index = _machHeaderToImageIndex[mh];
-        _bottomUpArray.push_back(index);
-        for (uint64_t initializer : op->initializerAddresses()) {
-            //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
-            dyld_cache_accelerator_initializer entry;
-            entry.functionOffset = (uint32_t)(initializer-cacheStartAddress);
-            entry.imageIndex = _machHeaderToImageIndex[mh];
-            _initializers.push_back(entry);
-        }
-    }
-
-    // build ordered list of DOF sections
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        assert(op != NULL);
-        unsigned imageIndex = _machHeaderToImageIndex[mh];
-        for (auto& sect : op->dofSections()) {
-            //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName());
-            dyld_cache_accelerator_dof entry;
-            entry.sectionAddress = sect->addr();
-            entry.sectionSize    = (uint32_t)sect->size();
-            entry.imageIndex     = imageIndex;
-            _dofSections.push_back(entry);
-        }
-    }
-
-    // register exports trie and weak binding info in each dylib with image extra info
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned index = _machHeaderToImageIndex[mh];
-        _extraInfo[index].exportsTrieAddr  = op->exportsTrieLinkEditOffset() + linkeditStartAddr;
-        _extraInfo[index].exportsTrieSize  = op->exportsTrieLinkEditSize();
-        _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr;
-        _extraInfo[index].weakBindingsSize = op->weakBindingLinkEditSize();
-    }
-
-    // record location of __DATA/__dyld section in libdyld.dylib
-    macho_header<P>* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"];
-    LinkeditOptimizer<P>* libdyldOp = _machHeaderToOptimizer[libdyldMH];
-    uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress();
-
-    // build range table for fast address->image lookups
-    for (macho_header<P>* mh : sortedMachHeaders) {
-        LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
-        unsigned imageIndex = _machHeaderToImageIndex[mh];
-        for (const macho_segment_command<P>* segCmd : op->segCmds()) {
-            if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
-                continue;
-            dyld_cache_range_entry entry;
-            entry.startAddress = segCmd->vmaddr();
-            entry.size         = (uint32_t)segCmd->vmsize();
-            entry.imageIndex   = imageIndex;
-            _rangeTable.push_back(entry);
-        }
-    }
-    std::sort(_rangeTable.begin(), _rangeTable.end(),
-              [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& rRange) -> bool {
-                return (lRange.startAddress < rRange.startAddress);
-              });
-
-    // build trie that maps install names to image index
-    std::vector<DylibIndexTrie::Entry> dylibEntrys;
-    for (auto &x : _dylibPathToMachHeader) {
-        const std::string& path = x.first;
-        unsigned index = _machHeaderToImageIndex[x.second];
-        dylibEntrys.push_back(DylibIndexTrie::Entry(path, DylibIndex(index)));
-    }
-    DylibIndexTrie dylibsTrie(dylibEntrys);
-    dylibsTrie.emit(_trieBytes);
-    while ( (_trieBytes.size() % 4) != 0 )
-        _trieBytes.push_back(0);
-
-    // fill out header
-    _acceleratorInfoHeader.version              = 1;
-    _acceleratorInfoHeader.imageExtrasCount     = (uint32_t)_extraInfo.size();
-    _acceleratorInfoHeader.imagesExtrasOffset   = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra);
-    _acceleratorInfoHeader.bottomUpListOffset   = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra);
-    _acceleratorInfoHeader.dylibTrieOffset      = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t);
-    _acceleratorInfoHeader.dylibTrieSize        = (uint32_t)_trieBytes.size();
-    _acceleratorInfoHeader.initializersOffset   = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer);
-    _acceleratorInfoHeader.initializersCount    = (uint32_t)_initializers.size();
-    _acceleratorInfoHeader.dofSectionsOffset    = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer);
-    _acceleratorInfoHeader.dofSectionsCount     = (uint32_t)_dofSections.size();
-    _acceleratorInfoHeader.reExportListOffset   = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof);
-    _acceleratorInfoHeader.reExportCount        = (uint32_t)_reExportArray.size();
-    _acceleratorInfoHeader.depListOffset        = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t);
-    _acceleratorInfoHeader.depListCount         = (uint32_t)_dependencyArray.size();
-    _acceleratorInfoHeader.rangeTableOffset     = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry);
-    _acceleratorInfoHeader.rangeTableCount      = (uint32_t)_rangeTable.size();
-    _acceleratorInfoHeader.dyldSectionAddr      = dyldSectionAddr;
-}
-
-
-template <typename P>
-void AcceleratorTables<P>::DepNode::computeDepth()
-{
-    if ( _depth != 0 )
-        return;
-    _depth = 1;
-    for (DepNode* node : _dependents) {
-        node->computeDepth();
-        if ( node->_depth >= _depth )
-            _depth = node->_depth + 1;
-    }
-}
-
-template <typename P>
-uint32_t AcceleratorTables<P>::totalSize() const
-{
-    return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14);
-}
-
-template <typename P>
-void AcceleratorTables<P>::copyTo(uint8_t* buffer)
-{
-    memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info));
-    memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset], &_extraInfo[0],       _extraInfo.size()*sizeof(dyld_cache_image_info_extra));
-    memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset], &_bottomUpArray[0],   _bottomUpArray.size()*sizeof(uint16_t));
-    memcpy(&buffer[_acceleratorInfoHeader.initializersOffset], &_initializers[0],    _initializers.size()*sizeof(dyld_cache_accelerator_initializer));
-    memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset], &_reExportArray[0],   _reExportArray.size()*sizeof(uint16_t));
-    memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset],  &_dofSections[0],     _dofSections.size()*sizeof(dyld_cache_accelerator_dof));
-    memcpy(&buffer[_acceleratorInfoHeader.depListOffset],      &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t));
-    memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset],   &_rangeTable[0],      _rangeTable.size()*sizeof(dyld_cache_range_entry));
-    memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset],    &_trieBytes[0],       _trieBytes.size());
-}
-
-
-
-template <typename P>
-LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag)
-: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag)
-{
-    _linkeditBias = (uint8_t*)cacheBuffer;
+template <typename P>
+LinkeditOptimizer<P>::LinkeditOptimizer(const void* containerBuffer, macho_header<P>* mh,
+                                        const char* dylibID, Diagnostics& diag)
+: _mh(mh), _dylibID(dylibID), _containerBuffer(containerBuffer), _diagnostics(diag)
+{
     const unsigned origLoadCommandsSize = mh->sizeofcmds();
     unsigned bytesRemaining = origLoadCommandsSize;
     unsigned removedCount = 0;
+    uint64_t    textSegAddr = 0;
+    int64_t     slide = 0;
     const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
     const uint32_t cmdCount = mh->ncmds();
     const macho_load_command<P>* cmd = cmds;
@@ -540,9 +222,6 @@
     for (uint32_t i = 0; i < cmdCount; ++i) {
         bool remove = false;
         switch (cmd->cmd()) {
-            case LC_ID_DYLIB:
-                _installName = ((macho_dylib_command<P>*)cmd)->name();
-                break;
             case LC_SYMTAB:
                 _symTabCmd = (macho_symtab_command<P>*)cmd;
                 break;
@@ -559,6 +238,10 @@
                 break;
             case LC_DATA_IN_CODE:
                 _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
+                break;
+            case LC_DYLD_EXPORTS_TRIE:
+                _exportTrieCmd = (macho_linkedit_data_command<P>*)cmd;
+                _exportInfoSize = _exportTrieCmd->datasize();
                 break;
             case LC_ROUTINES:
             case LC_ROUTINES_64:
@@ -579,10 +262,14 @@
             case macho_segment_command<P>::CMD:
                 segCmd = (macho_segment_command<P>*)cmd;
                 _segCmds.push_back(segCmd);
-                if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+                if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) {
+                    textSegAddr = segCmd->vmaddr();
+                    slide = (uint64_t)mh - textSegAddr;
+                }
+                else if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+                    _linkeditAddr        = segCmd->vmaddr();
+                    _linkeditBias        = (uint8_t*)mh + (_linkeditAddr - textSegAddr) - segCmd->fileoff();
                     _linkeditSize        = (uint32_t)segCmd->vmsize();
-                    _linkeditCacheOffset = (uint32_t)segCmd->fileoff();
-                    _linkeditAddr        = segCmd->vmaddr();
                 }
                 else if ( segCmd->nsects() > 0 ) {
                     macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
@@ -590,14 +277,22 @@
                     for (macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
                         const uint8_t type = sect->flags() & SECTION_TYPE;
                         if ( type == S_MOD_INIT_FUNC_POINTERS ) {
-                            const pint_t* inits = (pint_t*)((char*)cacheBuffer + sect->offset());
+                            const pint_t* inits = (pint_t*)(sect->addr()+slide);
                             const size_t count = sect->size() / sizeof(pint_t);
                             for (size_t j=0; j < count; ++j) {
                                 uint64_t func = P::getP(inits[j]);
                                 _initializerAddresses.push_back(func);
                             }
                         }
-                          else if ( type == S_DTRACE_DOF ) {
+                        else if ( type == S_INIT_FUNC_OFFSETS ) {
+                            const uint32_t* inits = (uint32_t*)(sect->addr()+slide);
+                            const size_t count = sect->size() / sizeof(uint32_t);
+                            for (size_t j=0; j < count; ++j) {
+                                uint32_t funcOffset = E::get32(inits[j]);
+                                _initializerAddresses.push_back(textSegAddr + funcOffset);
+                            }
+                        }
+                        else if ( type == S_DTRACE_DOF ) {
                             _dofSections.push_back(sect);
                         }
                         else if ( (strcmp(sect->sectname(), "__dyld") == 0) && (strncmp(sect->segname(), "__DATA", 6) == 0) ) {
@@ -606,6 +301,7 @@
                     }
                 }
                 break;
+            case LC_DYLD_CHAINED_FIXUPS:
             case LC_SEGMENT_SPLIT_INFO:
                 remove = true;
                 break;
@@ -626,6 +322,11 @@
     // update header
     mh->set_ncmds(cmdCount - removedCount);
     mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining);
+}
+
+template <typename P>
+void LinkeditOptimizer<P>::setStripMode(DylibStripMode stripMode) {
+    _stripMode = stripMode;
 }
 
 /*
@@ -731,20 +432,22 @@
     _symTabCmd->set_strsize(sharedSymbolStringsSize);
 
     // update dynamic symbol table to have proper offsets into shared symbol table
-    _dynSymTabCmd->set_ilocalsym(0);
-    _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount);
-    _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
-    _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount);
-    _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
-    _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount);
-    _dynSymTabCmd->set_tocoff(0);
-    _dynSymTabCmd->set_ntoc(0);
-    _dynSymTabCmd->set_modtaboff(0);
-    _dynSymTabCmd->set_nmodtab(0);
-    _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset);
-    _dynSymTabCmd->set_extreloff(0);
-    _dynSymTabCmd->set_locreloff(0);
-    _dynSymTabCmd->set_nlocrel(0);
+    if ( _dynSymTabCmd != nullptr ) {
+        _dynSymTabCmd->set_ilocalsym(0);
+        _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount);
+        _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
+        _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount);
+        _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
+        _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount);
+        _dynSymTabCmd->set_tocoff(0);
+        _dynSymTabCmd->set_ntoc(0);
+        _dynSymTabCmd->set_modtaboff(0);
+        _dynSymTabCmd->set_nmodtab(0);
+        _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset);
+        _dynSymTabCmd->set_extreloff(0);
+        _dynSymTabCmd->set_locreloff(0);
+        _dynSymTabCmd->set_nlocrel(0);
+    }
 
     // update dyld info
     if ( _dyldInfo != nullptr ) {
@@ -754,6 +457,8 @@
         _dyldInfo->set_weak_bind_off(_dyldInfo->weak_bind_size() ?  mergedLinkeditStartOffset + _newWeakBindingInfoOffset : 0 );
         _dyldInfo->set_lazy_bind_off(_dyldInfo->lazy_bind_size() ?  mergedLinkeditStartOffset + _newLazyBindingInfoOffset : 0 );
         _dyldInfo->set_export_off(mergedLinkeditStartOffset + _newExportInfoOffset);
+    } else if ( _exportTrieCmd != nullptr ) {
+        _exportTrieCmd->set_dataoff(mergedLinkeditStartOffset + _newExportInfoOffset);
     }
 
     // update function-starts
@@ -809,13 +514,15 @@
 template <typename P>
 void LinkeditOptimizer<P>::copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset)
 {
-    if ( _dyldInfo == nullptr )
+    if ( (_dyldInfo == nullptr) && (_exportTrieCmd == nullptr) )
         return;
-    unsigned size = _dyldInfo->export_size();
-    if ( size != 0 ) {
-        ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->export_off()], size);
+
+    uint32_t exportOffset = _exportTrieCmd ? _exportTrieCmd->dataoff() : _dyldInfo->export_off();
+    uint32_t exportSize   = _exportTrieCmd ? _exportTrieCmd->datasize() : _dyldInfo->export_size();
+    if ( exportSize != 0 ) {
+        ::memcpy(&newLinkEditContent[offset], &_linkeditBias[exportOffset], exportSize);
         _newExportInfoOffset = offset;
-        offset += size;
+        offset += exportSize;
     }
 }
 
@@ -848,11 +555,27 @@
                                             bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
                                             std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& localSymbolsStringPool)
 {
-    LocalSymbolInfo localInfo;
-    localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_cacheBuffer);
+    localSymbolInfos.push_back(LocalSymbolInfo());
+
+    LocalSymbolInfo& localInfo = localSymbolInfos.back();
+    localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_containerBuffer);
     localInfo.nlistStartIndex = (uint32_t)unmappedLocalSymbols.size();
     localInfo.nlistCount = 0;
     _newLocalSymbolsStartIndex = symbolIndex;
+    _newLocalSymbolCount = 0;
+
+    switch (_stripMode) {
+        case CacheBuilder::DylibStripMode::stripNone:
+        case CacheBuilder::DylibStripMode::stripExports:
+            break;
+        case CacheBuilder::DylibStripMode::stripLocals:
+        case CacheBuilder::DylibStripMode::stripAll:
+            return;
+    }
+
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
     const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
     const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
     const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()];
@@ -885,7 +608,6 @@
     }
     _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex;
     localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex;
-    localSymbolInfos.push_back(localInfo);
 }
 
 
@@ -893,6 +615,20 @@
 void LinkeditOptimizer<P>::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
 {
     _newExportedSymbolsStartIndex = symbolIndex;
+    _newExportedSymbolCount = 0;
+
+    switch (_stripMode) {
+        case CacheBuilder::DylibStripMode::stripNone:
+        case CacheBuilder::DylibStripMode::stripLocals:
+            break;
+        case CacheBuilder::DylibStripMode::stripExports:
+        case CacheBuilder::DylibStripMode::stripAll:
+            return;
+    }
+
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
     const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
     const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
     const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()];
@@ -921,6 +657,20 @@
 void LinkeditOptimizer<P>::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
 {
     _newImportedSymbolsStartIndex = symbolIndex;
+    _newImportedSymbolCount = 0;
+
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
+    switch (_stripMode) {
+        case CacheBuilder::DylibStripMode::stripNone:
+            break;
+        case CacheBuilder::DylibStripMode::stripLocals:
+        case CacheBuilder::DylibStripMode::stripExports:
+        case CacheBuilder::DylibStripMode::stripAll:
+            return;
+    }
+
     const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
     const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
     const macho_nlist<P>* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()];
@@ -945,9 +695,13 @@
 void LinkeditOptimizer<P>::copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset)
 {
     _newIndirectSymbolTableOffset = offset;
+
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
      const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
     uint32_t* newIndirectTable = (uint32_t*)&newLinkEditContent[offset];
-    for (int i=0; i < _dynSymTabCmd->nindirectsyms(); ++i) {
+    for (uint32_t i=0; i < _dynSymTabCmd->nindirectsyms(); ++i) {
         uint32_t symbolIndex = E::get32(indirectTable[i]);
         if ( (symbolIndex == INDIRECT_SYMBOL_ABS) || (symbolIndex == INDIRECT_SYMBOL_LOCAL) )
             E::set32(newIndirectTable[i], symbolIndex);
@@ -958,63 +712,63 @@
 }
 
 template <typename P>
-uint64_t mergeLinkedits(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector<LinkeditOptimizer<P>*>& optimizers, Diagnostics& diagnostics, dyld_cache_local_symbols_info** localsInfo)
+void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder,
+                                          CacheBuilder::UnmappedRegion* localSymbolsRegion,
+                                          std::vector<LinkeditOptimizer<P>*>& optimizers)
 {
     // allocate space for new linkedit data
-    uint32_t linkeditStartOffset = 0xFFFFFFFF;
-    uint32_t linkeditEndOffset = 0;
-    uint64_t linkeditStartAddr = 0;
-    for (LinkeditOptimizer<P>* op : optimizers) {
-        uint32_t leOffset = op->linkeditOffset();
-        if ( leOffset < linkeditStartOffset ) {
-            linkeditStartOffset = leOffset;
-            linkeditStartAddr = op->linkeditAddr();
-        }
-        uint32_t leEndOffset = op->linkeditOffset() + op->linkeditSize();
-        if ( leEndOffset > linkeditEndOffset )
-            linkeditEndOffset = leEndOffset;
-    }
-    uint64_t totalUnoptLinkeditsSize = linkeditEndOffset - linkeditStartOffset;
+    uint64_t totalUnoptLinkeditsSize = builder._readOnlyRegion.sizeInUse - builder._nonLinkEditReadOnlySize;
     uint8_t* newLinkEdit = (uint8_t*)calloc(totalUnoptLinkeditsSize, 1);
     SortedStringPool<P> stringPool;
     uint32_t offset = 0;
 
-    diagnostics.verbose("Merged LINKEDIT:\n");
+    builder._diagnostics.verbose("Merged LINKEDIT:\n");
 
     // copy weak binding info
     uint32_t startWeakBindInfosOffset = offset;
     for (LinkeditOptimizer<P>* op : optimizers) {
-        op->copyWeakBindingInfo(newLinkEdit, offset);
-    }
-    diagnostics.verbose("  weak bindings size:      %5uKB\n", (uint32_t)(offset-startWeakBindInfosOffset)/1024);
+        // Skip chained fixups as the in-place linked list isn't valid any more
+        const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+        if (!mf->hasChainedFixups())
+            op->copyWeakBindingInfo(newLinkEdit, offset);
+    }
+    builder._diagnostics.verbose("  weak bindings size:      %5uKB\n", (uint32_t)(offset-startWeakBindInfosOffset)/1024);
 
     // copy export info
     uint32_t startExportInfosOffset = offset;
     for (LinkeditOptimizer<P>* op : optimizers) {
         op->copyExportInfo(newLinkEdit, offset);
     }
-    diagnostics.verbose("  exports info size:       %5uKB\n", (uint32_t)(offset-startExportInfosOffset)/1024);
+    builder._diagnostics.verbose("  exports info size:       %5uKB\n", (uint32_t)(offset-startExportInfosOffset)/1024);
 
     // in theory, an optimized cache can drop the binding info
     if ( true ) {
         // copy binding info
         uint32_t startBindingsInfosOffset = offset;
         for (LinkeditOptimizer<P>* op : optimizers) {
-            op->copyBindingInfo(newLinkEdit, offset);
-        }
-        diagnostics.verbose("  bindings size:           %5uKB\n", (uint32_t)(offset-startBindingsInfosOffset)/1024);
+            // Skip chained fixups as the in-place linked list isn't valid any more
+            const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+            if (!mf->hasChainedFixups())
+                op->copyBindingInfo(newLinkEdit, offset);
+        }
+        builder._diagnostics.verbose("  bindings size:           %5uKB\n", (uint32_t)(offset-startBindingsInfosOffset)/1024);
 
        // copy lazy binding info
         uint32_t startLazyBindingsInfosOffset = offset;
         for (LinkeditOptimizer<P>* op : optimizers) {
-            op->copyLazyBindingInfo(newLinkEdit, offset);
-        }
-        diagnostics.verbose("  lazy bindings size:      %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
-    }
+            // Skip chained fixups as the in-place linked list isn't valid any more
+            const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+            if (!mf->hasChainedFixups())
+                op->copyLazyBindingInfo(newLinkEdit, offset);
+        }
+        builder._diagnostics.verbose("  lazy bindings size:      %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
+    }
+
+    bool unmapLocals = ( builder._options.localSymbolMode == DyldSharedCache::LocalSymbolsMode::unmap );
 
     // copy symbol table entries
     std::vector<macho_nlist<P>> unmappedLocalSymbols;
-    if ( dontMapLocalSymbols )
+    if ( unmapLocals )
         unmappedLocalSymbols.reserve(0x01000000);
     std::vector<LocalSymbolInfo> localSymbolInfos;
         localSymbolInfos.reserve(optimizers.size());
@@ -1024,7 +778,7 @@
     uint32_t sharedSymbolTableExportsCount = 0;
     uint32_t sharedSymbolTableImportsCount = 0;
     for (LinkeditOptimizer<P>* op : optimizers) {
-         op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, dontMapLocalSymbols,
+         op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, unmapLocals,
                              localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool);
         uint32_t x = symbolIndex;
         op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
@@ -1041,14 +795,14 @@
     for (LinkeditOptimizer<P>* op : optimizers) {
         op->copyFunctionStarts(newLinkEdit, offset);
     }
-    diagnostics.verbose("  function starts size:    %5uKB\n", (offset-startFunctionStartsOffset)/1024);
+    builder._diagnostics.verbose("  function starts size:    %5uKB\n", (offset-startFunctionStartsOffset)/1024);
 
     // copy data-in-code info
     uint32_t startDataInCodeOffset = offset;
     for (LinkeditOptimizer<P>* op : optimizers) {
         op->copyDataInCode(newLinkEdit, offset);
     }
-    diagnostics.verbose("  data in code size:       %5uKB\n", (offset-startDataInCodeOffset)/1024);
+    builder._diagnostics.verbose("  data in code size:       %5uKB\n", (offset-startDataInCodeOffset)/1024);
 
     // copy indirect symbol tables
     for (LinkeditOptimizer<P>* op : optimizers) {
@@ -1063,41 +817,18 @@
     uint32_t sharedSymbolStringsSize = stringPool.copyPoolAndUpdateOffsets((char*)&newLinkEdit[sharedSymbolStringsOffset], (macho_nlist<P>*)&newLinkEdit[sharedSymbolTableStartOffset]);
     offset += sharedSymbolStringsSize;
     uint32_t newLinkeditUnalignedSize = offset;
-    uint64_t newLinkeditEnd = align(linkeditStartOffset+newLinkeditUnalignedSize, 14);
-    diagnostics.verbose("  symbol table size:       %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
-    diagnostics.verbose("  symbol string pool size: %5uKB\n", sharedSymbolStringsSize/1024);
+    uint64_t newLinkeditAlignedSize = align(offset, 14);
+    builder._diagnostics.verbose("  symbol table size:       %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
+    builder._diagnostics.verbose("  symbol string pool size: %5uKB\n", sharedSymbolStringsSize/1024);
 
     // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content
-    diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
-    ::memcpy((char*)cache + linkeditStartOffset, newLinkEdit, newLinkeditUnalignedSize);
-    ::bzero((char*)cache + linkeditStartOffset+newLinkeditUnalignedSize, totalUnoptLinkeditsSize-newLinkeditUnalignedSize);
+    builder._diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
+    ::memcpy(builder._readOnlyRegion.buffer+builder._nonLinkEditReadOnlySize, newLinkEdit, newLinkeditAlignedSize);
     ::free(newLinkEdit);
-
-    // If making cache for customers, add extra accelerator tables for dyld
-    if ( addAcceleratorTables ) {
-        AcceleratorTables<P> tables(cache, linkeditStartAddr, diagnostics, optimizers);
-        uint32_t tablesSize = tables.totalSize();
-        if ( tablesSize < (totalUnoptLinkeditsSize-newLinkeditUnalignedSize) ) {
-            tables.copyTo((uint8_t*)cache+newLinkeditEnd);
-            newLinkeditEnd += tablesSize;
-            uint64_t accelInfoAddr = align(linkeditStartAddr + newLinkeditUnalignedSize, 14);
-            cache->header.accelerateInfoAddr = accelInfoAddr;
-            cache->header.accelerateInfoSize = tablesSize;
-            diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
-       }
-        else {
-            diagnostics.warning("not enough room to add dyld accelerator tables");
-        }
-    }
-
-    // update mapping to reduce linkedit size
-    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
-    mappings[2].size = newLinkeditEnd - mappings[2].fileOffset;
+    builder._readOnlyRegion.sizeInUse = builder._nonLinkEditReadOnlySize + newLinkeditAlignedSize;
 
     // overwrite end of un-opt linkedits to create a new unmapped region for local symbols
-    uint64_t newFileSize = newLinkeditEnd;
-    if ( dontMapLocalSymbols ) {
-        typedef typename P::E   E;
+    if ( unmapLocals ) {
         const uint32_t entriesOffset = sizeof(dyld_cache_local_symbols_info);
         const uint32_t entriesCount  = (uint32_t)localSymbolInfos.size();
         const uint32_t nlistOffset   = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyld_cache_local_symbols_info), 4); // 16-byte align start
@@ -1106,50 +837,60 @@
         const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist<P>);
         // allocate buffer for local symbols
         const size_t localsBufferSize = align(stringsOffset + stringsSize, 14);
-        dyld_cache_local_symbols_info* infoHeader = (dyld_cache_local_symbols_info*)malloc(localsBufferSize);
-        // fill in header info
-        infoHeader->nlistOffset       = nlistOffset;
-        infoHeader->nlistCount        = nlistCount;
-        infoHeader->stringsOffset     = stringsOffset;
-        infoHeader->stringsSize       = stringsSize;
-        infoHeader->entriesOffset     = entriesOffset;
-        infoHeader->entriesCount      = entriesCount;
-        // copy info for each dylib
-        dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)(((uint8_t*)infoHeader)+entriesOffset);
-        for (int i=0; i < entriesCount; ++i) {
-            entries[i].dylibOffset        = localSymbolInfos[i].dylibOffset;
-            entries[i].nlistStartIndex    = localSymbolInfos[i].nlistStartIndex;
-            entries[i].nlistCount         = localSymbolInfos[i].nlistCount;
-        }
-        // copy nlists
-        macho_nlist<P>* newLocalsSymbolTable = (macho_nlist<P>*)(((uint8_t*)infoHeader)+nlistOffset);
-        ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
-        // copy string pool
-        localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
-        // return buffer of local symbols, caller to free() it
-        *localsInfo = infoHeader;
+        vm_address_t localsBuffer;
+        if ( ::vm_allocate(mach_task_self(), &localsBuffer, localsBufferSize, VM_FLAGS_ANYWHERE) == 0 ) {
+            dyld_cache_local_symbols_info* infoHeader = (dyld_cache_local_symbols_info*)localsBuffer;
+            // fill in header info
+            infoHeader->nlistOffset       = nlistOffset;
+            infoHeader->nlistCount        = nlistCount;
+            infoHeader->stringsOffset     = stringsOffset;
+            infoHeader->stringsSize       = stringsSize;
+            infoHeader->entriesOffset     = entriesOffset;
+            infoHeader->entriesCount      = entriesCount;
+            // copy info for each dylib
+            dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)(((uint8_t*)infoHeader)+entriesOffset);
+            for (uint32_t i=0; i < entriesCount; ++i) {
+                entries[i].dylibOffset        = localSymbolInfos[i].dylibOffset;
+                entries[i].nlistStartIndex    = localSymbolInfos[i].nlistStartIndex;
+                entries[i].nlistCount         = localSymbolInfos[i].nlistCount;
+            }
+            // copy nlists
+            macho_nlist<P>* newLocalsSymbolTable = (macho_nlist<P>*)(localsBuffer+nlistOffset);
+            ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
+            // copy string pool
+            localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
+            // return buffer of local symbols, caller to free() it
+            localSymbolsRegion->buffer      = (uint8_t*)localsBuffer;
+            localSymbolsRegion->bufferSize  = localsBufferSize;
+            localSymbolsRegion->sizeInUse   = localsBufferSize;
+        }
+        else {
+            builder._diagnostics.warning("could not allocate local symbols");
+        }
     }
 
     // update all load commands to new merged layout
+    uint64_t linkeditsUnslidStartAddr = builder._readOnlyRegion.unslidLoadAddress + builder._nonLinkEditReadOnlySize;
+    uint32_t linkeditsCacheFileOffset = (uint32_t)(builder._readOnlyRegion.cacheFileOffset + builder._nonLinkEditReadOnlySize);
     for (LinkeditOptimizer<P>* op : optimizers) {
-        op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset,
+        op->updateLoadCommands(linkeditsCacheFileOffset, linkeditsUnslidStartAddr, newLinkeditUnalignedSize,
                                sharedSymbolTableStartOffset, sharedSymbolTableCount,
                                sharedSymbolStringsOffset, sharedSymbolStringsSize);
     }
-
-    return newFileSize;
-}
-
-} // anonymous namespace
-
-template <typename P>
-uint64_t optimizeLinkedit(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo)
+}
+
+
+template <typename P>
+void LinkeditOptimizer<P>::optimizeLinkedit(CacheBuilder& builder, const void* containerBuffer,
+                                            CacheBuilder::UnmappedRegion* localSymbolsRegion,
+                                            const std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>>& images)
 {
     // construct a LinkeditOptimizer for each image
     __block std::vector<LinkeditOptimizer<P>*> optimizers;
-    cache->forEachImage(^(const mach_header* mh, const char*) {
-        optimizers.push_back(new LinkeditOptimizer<P>(cache, (macho_header<P>*)mh, diag));
-    });
+    for (std::tuple<const mach_header*, const char*, DylibStripMode> image : images) {
+        optimizers.push_back(new LinkeditOptimizer<P>(containerBuffer, (macho_header<P>*)std::get<0>(image), std::get<1>(image), builder._diagnostics));
+        optimizers.back()->setStripMode(std::get<2>(image));
+    }
 #if 0
     // add optimizer for each branch pool
     for (uint64_t poolOffset : branchPoolOffsets) {
@@ -1158,24 +899,26 @@
     }
 #endif
     // merge linkedit info
-    uint64_t newFileSize = mergeLinkedits(cache, dontMapLocalSymbols, addAcceleratorTables, optimizers, diag, localsInfo);
+    mergeLinkedits(builder, localSymbolsRegion, optimizers);
 
     // delete optimizers
     for (LinkeditOptimizer<P>* op : optimizers)
         delete op;
-
-    return newFileSize;
-}
-
-uint64_t optimizeLinkedit(DyldSharedCache* cache, bool is64, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo)
-{
-    if ( is64) {
-        return optimizeLinkedit<Pointer64<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+}
+
+void CacheBuilder::optimizeLinkedit(UnmappedRegion* localSymbolsRegion,
+                                    const std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>>& images)
+{
+    const void* buffer = (const void*)_fullAllocatedBuffer;
+    if ( _is64 ) {
+        return LinkeditOptimizer<Pointer64<LittleEndian>>::optimizeLinkedit(*this, buffer,
+                                                                            localSymbolsRegion, images);
     }
     else {
-        return optimizeLinkedit<Pointer32<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
-    }
-}
-
-
-
+         return LinkeditOptimizer<Pointer32<LittleEndian>>::optimizeLinkedit(*this, buffer,
+                                                                             localSymbolsRegion, images);
+    }
+}
+
+
+