OptimizerLinkedit.cpp [plain text]
#include "mega-dylib-utils.h"
#include "MachOFileAbstraction.hpp"
#include "Logging.h"
#include <dirent.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <assert.h>
#include <fstream>
#include <iostream>
#include <string>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include "dyld_cache_config.h"
#include "Trie.hpp"
#if !NEW_CACHE_FILE_FORMAT
#include "CacheFileAbstraction.hpp"
#endif
#define ALIGN_AS_TYPE(value, type) \
((value + alignof(type) - 1) & (-alignof(type)))
namespace {
template <typename P>
class SortedStringPool
{
public:
void add(uint32_t symbolIndex, const char* symbolName) {
_map[symbolName].push_back(symbolIndex);
}
uint32_t copyPoolAndUpdateOffsets(char* dstStringPool, macho_nlist<P>* symbolTable) {
std::vector<std::string> allStrings;
allStrings.reserve(_map.size());
for (auto& entry : _map) {
allStrings.push_back(entry.first);
}
std::sort(allStrings.begin(), allStrings.end());
dstStringPool[0] = '\0'; uint32_t poolOffset = 1;
for (const std::string& symName : allStrings) {
strcpy(&dstStringPool[poolOffset], symName.c_str());
for (uint32_t symbolIndex : _map[symName]) {
symbolTable[symbolIndex].set_n_strx(poolOffset);
}
poolOffset += symName.size() + 1;
}
return poolOffset;
}
private:
std::unordered_map<std::string, std::vector<uint32_t>> _map;
};
struct LocalSymbolInfo
{
uint32_t dylibOffset;
uint32_t nlistStartIndex;
uint32_t nlistCount;
};
template <typename P>
class LinkeditOptimizer {
public:
LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh);
uint32_t linkeditSize() { return _linkeditSize; }
uint32_t linkeditOffset() { return _linkeditCacheOffset; }
uint64_t linkeditAddr() { return _linkeditAddr; }
const char* installName() { return _installName; }
void copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
void copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
void copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
void copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset);
void copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
void copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex);
void copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& localSymbolsStringPool);
void copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset);
void copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset);
void copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset);
void updateLoadCommands(uint32_t linkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize,
uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount,
uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize);
macho_header<P>* machHeader() { return _mh; }
const std::vector<const char*> getDownwardDependents() { return _downDependentPaths; }
const std::vector<const char*> getAllDependents() { return _allDependentPaths; }
const std::vector<const char*> getReExportPaths() { return _reExportPaths; }
const std::vector<uint64_t> initializerAddresses() { return _initializerAddresses; }
const std::vector<macho_section<P>*> dofSections() { return _dofSections; }
uint32_t exportsTrieLinkEditOffset() { return _newExportInfoOffset; }
uint32_t exportsTrieLinkEditSize() { return _exportInfoSize; }
uint32_t weakBindingLinkEditOffset() { return _newWeakBindingInfoOffset; }
uint32_t weakBindingLinkEditSize() { return _newWeakBindingSize; }
uint64_t dyldSectionAddress() { return _dyldSectionAddr; }
const std::vector<macho_segment_command<P>*>& segCmds() { return _segCmds; }
private:
typedef typename P::uint_t pint_t;
typedef typename P::E E;
macho_header<P>* _mh;
void* _cacheBuffer;
uint32_t _linkeditSize = 0;
uint32_t _linkeditCacheOffset = 0;
uint64_t _linkeditAddr = 0;
const uint8_t* _linkeditBias = nullptr;
const char* _installName = 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>* _functionStartsCmd = nullptr;
macho_linkedit_data_command<P>* _dataInCodeCmd = nullptr;
std::vector<macho_segment_command<P>*> _segCmds;
std::unordered_map<uint32_t,uint32_t> _oldToNewSymbolIndexes;
std::vector<const char*> _reExportPaths;
std::vector<const char*> _downDependentPaths;
std::vector<const char*> _allDependentPaths;
std::vector<uint64_t> _initializerAddresses;
std::vector<macho_section<P>*> _dofSections;
uint32_t _newWeakBindingInfoOffset = 0;
uint32_t _newLazyBindingInfoOffset = 0;
uint32_t _newBindingInfoOffset = 0;
uint32_t _newExportInfoOffset = 0;
uint32_t _exportInfoSize = 0;
uint32_t _newWeakBindingSize = 0;
uint32_t _newExportedSymbolsStartIndex = 0;
uint32_t _newExportedSymbolCount = 0;
uint32_t _newImportedSymbolsStartIndex = 0;
uint32_t _newImportedSymbolCount = 0;
uint32_t _newLocalSymbolsStartIndex = 0;
uint32_t _newLocalSymbolCount = 0;
uint32_t _newFunctionStartsOffset = 0;
uint32_t _newDataInCodeOffset = 0;
uint32_t _newIndirectSymbolTableOffset = 0;
uint64_t _dyldSectionAddr = 0;
};
template <typename P>
class AcceleratorTables {
public:
AcceleratorTables(void* cacheBuffer, uint64_t linkeditStartAddr, 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, 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<dyldCacheImageInfoExtra<E>> _extraInfo;
std::vector<uint8_t> _trieBytes;
std::vector<uint16_t> _reExportArray;
std::vector<uint16_t> _dependencyArray;
std::vector<uint16_t> _bottomUpArray;
std::vector<dyldCacheAcceleratorInitializer<E>> _initializers;
std::vector<dyldCacheAcceleratorDOFEntry<E>> _dofSections;
std::vector<dyldCacheAcceleratorRangeEntry<E>> _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;
dyldCacheAcceleratorInfo<E> _acceleratorInfoHeader;
};
template <typename P>
void AcceleratorTables<P>::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables<P>::DepNode* target, struct AcceleratorTables<P>::NodeChain& chain,
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 == node )
foundCycle = true;
}
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;
}
warning("%s", msg.c_str());
return;
}
if ( visitedNodes.count(node) )
continue;
NodeChain nextChain;
nextChain.prev = &chain;
nextChain.node = node;
verifyUnreachable(target, nextChain, visitedNodes, node->_dependents);
visitedNodes.insert(node);
}
}
const uint16_t kBranchIslandDylibIndex = 0x7FFF;
template <typename P>
AcceleratorTables<P>::AcceleratorTables(void* cacheBuffer, uint64_t linkeditStartAddr, const std::vector<LinkeditOptimizer<P>*>& optimizers)
{
for ( LinkeditOptimizer<P>* op : optimizers ) {
_machHeaderToOptimizer[op->machHeader()] = op;
}
uint64_t cacheStartAddress;
#if NEW_CACHE_FILE_FORMAT
#error new format support not implemented
#else
typedef typename P::E E;
const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cacheBuffer;
const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)((uint8_t*)cacheBuffer + header->mappingOffset());
cacheStartAddress = mappings[0].address();
const dyldCacheImageInfo<E>* images = (dyldCacheImageInfo<E>*)((uint8_t*)cacheBuffer + header->imagesOffset());
const unsigned imageCount = header->imagesCount();
for (unsigned i=0; i < imageCount; ++i) {
uint64_t segCacheFileOffset = images[i].address() - cacheStartAddress;
macho_header<P>* mhMapped = (macho_header<P>*)((uint8_t*)cacheBuffer+segCacheFileOffset);
const char* path = (char*)cacheBuffer + images[i].pathFileOffset();
_dylibPathToMachHeader[path] = mhMapped;
if ( images[i].pathFileOffset() > segCacheFileOffset )
_machHeaderToImageIndex[mhMapped] = i;
}
#endif
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];
assert(depMH != NULL);
DepNode* depNode = &_depDAG[depMH];
node._dependents.push_back(depNode);
}
}
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, visitedNodes, node->_dependents);
}
for (auto& entry : _depDAG) {
entry.second.computeDepth();
}
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);
});
dyldCacheImageInfoExtra<E> emptyExtra;
emptyExtra.set_exportsTrieAddr(0);
emptyExtra.set_weakBindingsAddr(0);
emptyExtra.set_exportsTrieSize(0);
emptyExtra.set_weakBindingsSize(0);
emptyExtra.set_dependentsStartArrayIndex(0);
emptyExtra.set_reExportsStartArrayIndex(0);
_extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra);
_dependencyArray.push_back(0xFFFF); for (macho_header<P>* mh : sortedMachHeaders) {
LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
unsigned index = _machHeaderToImageIndex[mh];
auto depPaths = op->getAllDependents();
if ( depPaths.empty() ) {
_extraInfo[index].set_dependentsStartArrayIndex(0);
}
else {
_extraInfo[index].set_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); }
}
_reExportArray.push_back(0xFFFF); for (macho_header<P>* mh : sortedMachHeaders) {
LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
unsigned index = _machHeaderToImageIndex[mh];
auto reExPaths = op->getReExportPaths();
if ( reExPaths.empty() ) {
_extraInfo[index].set_reExportsStartArrayIndex(0);
}
else {
_extraInfo[index].set_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); }
}
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()) {
dyldCacheAcceleratorInitializer<E> entry;
entry.set_functionOffset((uint32_t)(initializer-cacheStartAddress));
entry.set_imageIndex(_machHeaderToImageIndex[mh]);
_initializers.push_back(entry);
}
}
for (macho_header<P>* mh : sortedMachHeaders) {
LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
assert(op != NULL);
unsigned imageIndex = _machHeaderToImageIndex[mh];
for (auto& sect : op->dofSections()) {
dyldCacheAcceleratorDOFEntry<E> entry;
entry.set_sectionAddress(sect->addr());
entry.set_sectionSize((uint32_t)sect->size());
entry.set_imageIndex(imageIndex);
_dofSections.push_back(entry);
}
}
for (macho_header<P>* mh : sortedMachHeaders) {
LinkeditOptimizer<P>* op = _machHeaderToOptimizer[mh];
unsigned index = _machHeaderToImageIndex[mh];
_extraInfo[index].set_exportsTrieAddr(op->exportsTrieLinkEditOffset() + linkeditStartAddr);
_extraInfo[index].set_exportsTrieSize(op->exportsTrieLinkEditSize());
_extraInfo[index].set_weakBindingsAddr(op->weakBindingLinkEditOffset() + linkeditStartAddr);
_extraInfo[index].set_weakBindingsSize(op->weakBindingLinkEditSize());
}
macho_header<P>* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"];
LinkeditOptimizer<P>* libdyldOp = _machHeaderToOptimizer[libdyldMH];
uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress();
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;
dyldCacheAcceleratorRangeEntry<E> entry;
entry.set_startAddress(segCmd->vmaddr());
entry.set_size((uint32_t)segCmd->vmsize());
entry.set_imageIndex(imageIndex);
_rangeTable.push_back(entry);
}
}
std::sort(_rangeTable.begin(), _rangeTable.end(),
[&](const dyldCacheAcceleratorRangeEntry<E>& lRange, const dyldCacheAcceleratorRangeEntry<E>& rRange) -> bool {
return (lRange.startAddress() < rRange.startAddress());
});
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);
dyldCacheAcceleratorInfo<E>& h = _acceleratorInfoHeader;
h.set_version(1);
h.set_imageExtrasCount((uint32_t)_extraInfo.size());
h.set_imagesExtrasOffset(ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra));
h.set_bottomUpListOffset(h.imagesExtrasOffset() + h.imageExtrasCount()*sizeof(dyld_cache_image_info_extra));
h.set_dylibTrieOffset(h.bottomUpListOffset() + h.imageExtrasCount()*sizeof(uint16_t));
h.set_dylibTrieSize((uint32_t)_trieBytes.size());
h.set_initializersOffset(ALIGN_AS_TYPE(h.dylibTrieOffset() + h.dylibTrieSize(), dyld_cache_accelerator_initializer));
h.set_initializersCount((uint32_t)_initializers.size());
h.set_dofSectionsOffset(ALIGN_AS_TYPE(h.initializersOffset() + h.initializersCount()*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer));
h.set_dofSectionsCount((uint32_t)_dofSections.size());
h.set_reExportListOffset(ALIGN_AS_TYPE(h.dofSectionsOffset() + h.dofSectionsCount()*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof));
h.set_reExportCount((uint32_t)_reExportArray.size());
h.set_depListOffset(ALIGN_AS_TYPE(h.reExportListOffset() + h.reExportCount()*sizeof(uint16_t), uint16_t));
h.set_depListCount((uint32_t)_dependencyArray.size());
h.set_rangeTableOffset(ALIGN_AS_TYPE(h.depListOffset() + h.depListCount()*sizeof(uint16_t), dyld_cache_range_entry));
h.set_rangeTableCount((uint32_t)_rangeTable.size());
h.set_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)
: _mh(mh), _cacheBuffer(cacheBuffer)
{
_linkeditBias = (uint8_t*)cacheBuffer;
const unsigned origLoadCommandsSize = mh->sizeofcmds();
unsigned bytesRemaining = origLoadCommandsSize;
unsigned removedCount = 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;
const macho_dylib_command<P>* dylibCmd;
const macho_routines_command<P>* routinesCmd;
macho_segment_command<P>* segCmd;
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;
case LC_DYSYMTAB:
_dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
break;
case LC_DYLD_INFO:
case LC_DYLD_INFO_ONLY:
_dyldInfo = (macho_dyld_info_command<P>*)cmd;
_exportInfoSize = _dyldInfo->export_size();
break;
case LC_FUNCTION_STARTS:
_functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
break;
case LC_DATA_IN_CODE:
_dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
break;
case LC_ROUTINES:
case LC_ROUTINES_64:
routinesCmd = (macho_routines_command<P>*)cmd;
_initializerAddresses.push_back(routinesCmd->init_address());
break;
case LC_REEXPORT_DYLIB:
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_LOAD_UPWARD_DYLIB:
dylibCmd = (macho_dylib_command<P>*)cmd;
_allDependentPaths.push_back(dylibCmd->name());
if ( cmd->cmd() != LC_LOAD_UPWARD_DYLIB )
_downDependentPaths.push_back(dylibCmd->name());
if ( cmd->cmd() == LC_REEXPORT_DYLIB )
_reExportPaths.push_back(dylibCmd->name());
break;
case macho_segment_command<P>::CMD:
segCmd = (macho_segment_command<P>*)cmd;
_segCmds.push_back(segCmd);
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
_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>));
macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
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 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 ) {
_dofSections.push_back(sect);
}
else if ( (strcmp(sect->sectname(), "__dyld") == 0) && (strncmp(sect->segname(), "__DATA", 6) == 0) ) {
_dyldSectionAddr = sect->addr();
}
}
}
break;
case LC_SEGMENT_SPLIT_INFO:
remove = true;
break;
}
uint32_t cmdSize = cmd->cmdsize();
macho_load_command<P>* nextCmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmdSize);
if ( remove ) {
::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
++removedCount;
}
else {
bytesRemaining -= cmdSize;
cmd = nextCmd;
}
}
::bzero((void*)cmd, bytesRemaining);
mh->set_ncmds(cmdCount - removedCount);
mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining);
}
template <typename P>
void LinkeditOptimizer<P>::updateLoadCommands(uint32_t mergedLinkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize,
uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount,
uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize)
{
for (macho_segment_command<P>* segCmd : _segCmds) {
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
segCmd->set_vmaddr(mergedLinkeditAddr);
segCmd->set_vmsize(newLinkeditSize);
segCmd->set_fileoff(mergedLinkeditStartOffset);
segCmd->set_filesize(newLinkeditSize);
}
else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) {
segCmd->set_fileoff(0);
}
}
_symTabCmd->set_symoff(mergedLinkeditStartOffset + sharedSymbolTableStartOffset + _newLocalSymbolsStartIndex*sizeof(macho_nlist<P>));
_symTabCmd->set_nsyms(_newLocalSymbolCount+_newExportedSymbolCount+_newImportedSymbolCount);
_symTabCmd->set_stroff(mergedLinkeditStartOffset + sharedSymbolStringsOffset);
_symTabCmd->set_strsize(sharedSymbolStringsSize);
_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 ( _dyldInfo != nullptr ) {
_dyldInfo->set_rebase_off(0);
_dyldInfo->set_rebase_size(0);
_dyldInfo->set_bind_off(_dyldInfo->bind_size() ? mergedLinkeditStartOffset + _newBindingInfoOffset : 0);
_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);
}
if ( _functionStartsCmd != nullptr )
_functionStartsCmd->set_dataoff(mergedLinkeditStartOffset+_newFunctionStartsOffset);
if ( _dataInCodeCmd != nullptr )
_dataInCodeCmd->set_dataoff(mergedLinkeditStartOffset+_newDataInCodeOffset);
}
template <typename P>
void LinkeditOptimizer<P>::copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
{
if ( _dyldInfo == nullptr )
return;
unsigned size = _dyldInfo->weak_bind_size();
if ( size != 0 ) {
::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->weak_bind_off()], size);
_newWeakBindingInfoOffset = offset;
_newWeakBindingSize = size;
offset += size;
}
}
template <typename P>
void LinkeditOptimizer<P>::copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
{
if ( _dyldInfo == nullptr )
return;
unsigned size = _dyldInfo->lazy_bind_size();
if ( size != 0 ) {
::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->lazy_bind_off()], size);
_newLazyBindingInfoOffset = offset;
offset += size;
}
}
template <typename P>
void LinkeditOptimizer<P>::copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset)
{
if ( _dyldInfo == nullptr )
return;
unsigned size = _dyldInfo->bind_size();
if ( size != 0 ) {
::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->bind_off()], size);
_newBindingInfoOffset = offset;
offset += size;
}
}
template <typename P>
void LinkeditOptimizer<P>::copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset)
{
if ( _dyldInfo == nullptr )
return;
unsigned size = _dyldInfo->export_size();
if ( size != 0 ) {
::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->export_off()], size);
_newExportInfoOffset = offset;
offset += size;
}
}
template <typename P>
void LinkeditOptimizer<P>::copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset)
{
if ( _functionStartsCmd == nullptr )
return;
unsigned size = _functionStartsCmd->datasize();
::memcpy(&newLinkEditContent[offset], &_linkeditBias[_functionStartsCmd->dataoff()], size);
_newFunctionStartsOffset = offset;
offset += size;
}
template <typename P>
void LinkeditOptimizer<P>::copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset)
{
if ( _dataInCodeCmd == nullptr )
return;
unsigned size = _dataInCodeCmd->datasize();
::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dataInCodeCmd->dataoff()], size);
_newDataInCodeOffset = offset;
offset += size;
}
template <typename P>
void LinkeditOptimizer<P>::copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex,
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);
localInfo.nlistStartIndex = (uint32_t)unmappedLocalSymbols.size();
localInfo.nlistCount = 0;
_newLocalSymbolsStartIndex = symbolIndex;
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()];
const macho_nlist<P>* const lastExport = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
for (const macho_nlist<P>* entry = firstExport; entry < lastExport; ++entry) {
if ( (entry->n_type() & N_TYPE) != N_SECT)
continue;
if ( (entry->n_type() & N_STAB) != 0)
continue;
const char* name = &strings[entry->n_strx()];
macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
*newSymbolEntry = *entry;
if ( redact ) {
if ( entry->n_sect() == 1 ) {
stringPool.add(symbolIndex, "<redacted>");
++symbolIndex;
offset += sizeof(macho_nlist<P>);
}
localSymbolsStringPool.add((uint32_t)unmappedLocalSymbols.size(), name);
unmappedLocalSymbols.push_back(*entry);
unmappedLocalSymbols.back().set_n_strx(0);
}
else {
stringPool.add(symbolIndex, name);
++symbolIndex;
offset += sizeof(macho_nlist<P>);
}
}
_newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex;
localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex;
localSymbolInfos.push_back(localInfo);
}
template <typename P>
void LinkeditOptimizer<P>::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
{
_newExportedSymbolsStartIndex = symbolIndex;
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()];
const macho_nlist<P>* const lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
uint32_t oldSymbolIndex = _dynSymTabCmd->iextdefsym();
for (const macho_nlist<P>* entry = firstExport; entry < lastExport; ++entry, ++oldSymbolIndex) {
if ( (entry->n_type() & N_TYPE) != N_SECT)
continue;
const char* name = &strings[entry->n_strx()];
if ( strncmp(name, ".objc_", 6) == 0 )
continue;
if ( strncmp(name, "$ld$", 4) == 0 )
continue;
macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
*newSymbolEntry = *entry;
newSymbolEntry->set_n_strx(0);
stringPool.add(symbolIndex, name);
_oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
++symbolIndex;
offset += sizeof(macho_nlist<P>);
}
_newExportedSymbolCount = symbolIndex - _newExportedSymbolsStartIndex;
}
template <typename P>
void LinkeditOptimizer<P>::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
{
_newImportedSymbolsStartIndex = symbolIndex;
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()];
const macho_nlist<P>* const lastImport = &symbolTable[_dynSymTabCmd->iundefsym()+_dynSymTabCmd->nundefsym()];
uint32_t oldSymbolIndex = _dynSymTabCmd->iundefsym();
for (const macho_nlist<P>* entry = firstImport; entry < lastImport; ++entry, ++oldSymbolIndex) {
if ( (entry->n_type() & N_TYPE) != N_UNDF)
continue;
const char* name = &strings[entry->n_strx()];
macho_nlist<P>* newSymbolEntry = (macho_nlist<P>*)&newLinkEditContent[offset];
*newSymbolEntry = *entry;
newSymbolEntry->set_n_strx(0);
stringPool.add(symbolIndex, name);
_oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex;
++symbolIndex;
offset += sizeof(macho_nlist<P>);
}
_newImportedSymbolCount = symbolIndex - _newImportedSymbolsStartIndex;
}
template <typename P>
void LinkeditOptimizer<P>::copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset)
{
_newIndirectSymbolTableOffset = offset;
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) {
uint32_t symbolIndex = E::get32(indirectTable[i]);
if ( (symbolIndex == INDIRECT_SYMBOL_ABS) || (symbolIndex == INDIRECT_SYMBOL_LOCAL) )
E::set32(newIndirectTable[i], symbolIndex);
else
E::set32(newIndirectTable[i], _oldToNewSymbolIndexes[symbolIndex]);
offset += sizeof(uint32_t);
}
}
template <typename P>
uint64_t mergeLinkedits(SharedCache& cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector<LinkeditOptimizer<P>*>& optimizers)
{
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;
uint8_t* newLinkEdit = (uint8_t*)calloc(totalUnoptLinkeditsSize, 1);
SortedStringPool<P> stringPool;
uint32_t offset = 0;
verboseLog("Merged LINKEDIT:");
uint32_t startWeakBindInfosOffset = offset;
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyWeakBindingInfo(newLinkEdit, offset);
}
verboseLog(" weak bindings size: %5uKB", (offset-startWeakBindInfosOffset)/1024);
uint32_t startExportInfosOffset = offset;
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyExportInfo(newLinkEdit, offset);
}
verboseLog(" exports info size: %5uKB", (offset-startExportInfosOffset)/1024);
if ( true ) {
uint32_t startBindingsInfosOffset = offset;
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyBindingInfo(newLinkEdit, offset);
}
verboseLog(" bindings size: %5uKB", (offset-startBindingsInfosOffset)/1024);
uint32_t startLazyBindingsInfosOffset = offset;
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyLazyBindingInfo(newLinkEdit, offset);
}
verboseLog(" lazy bindings size: %5uKB", (offset-startLazyBindingsInfosOffset)/1024);
}
std::vector<macho_nlist<P>> unmappedLocalSymbols;
if ( dontMapLocalSymbols )
unmappedLocalSymbols.reserve(0x01000000);
std::vector<LocalSymbolInfo> localSymbolInfos;
localSymbolInfos.reserve(optimizers.size());
SortedStringPool<P> localSymbolsStringPool;
uint32_t symbolIndex = 0;
const uint32_t sharedSymbolTableStartOffset = offset;
uint32_t sharedSymbolTableExportsCount = 0;
uint32_t sharedSymbolTableImportsCount = 0;
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, dontMapLocalSymbols,
localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool);
uint32_t x = symbolIndex;
op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
sharedSymbolTableExportsCount += (symbolIndex-x);
uint32_t y = symbolIndex;
op->copyImportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
sharedSymbolTableImportsCount += (symbolIndex-y);
}
uint32_t sharedSymbolTableCount = symbolIndex;
const uint32_t sharedSymbolTableEndOffset = offset;
uint32_t startFunctionStartsOffset = offset;
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyFunctionStarts(newLinkEdit, offset);
}
verboseLog(" function starts size: %5uKB", (offset-startFunctionStartsOffset)/1024);
uint32_t startDataInCodeOffset = offset;
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyDataInCode(newLinkEdit, offset);
}
verboseLog(" data in code size: %5uB", offset-startDataInCodeOffset);
for (LinkeditOptimizer<P>* op : optimizers) {
op->copyIndirectSymbolTable(newLinkEdit, offset);
}
if ( (offset % sizeof(typename P::uint_t)) != 0 )
offset += 4;
uint32_t sharedSymbolStringsOffset = offset;
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);
verboseLog(" symbol table size: %5uKB (%d exports, %d imports)", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
verboseLog(" symbol string pool size: %5uKB", sharedSymbolStringsSize/1024);
verboseLog("LINKEDITS optimized from %uMB to %uMB", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
::memcpy((char*)cache.buffer().get()+linkeditStartOffset, newLinkEdit, newLinkeditUnalignedSize);
::bzero((char*)cache.buffer().get()+linkeditStartOffset+newLinkeditUnalignedSize, totalUnoptLinkeditsSize-newLinkeditUnalignedSize);
::free(newLinkEdit);
if ( addAcceleratorTables ) {
AcceleratorTables<P> tables(cache.buffer().get(), linkeditStartAddr, optimizers);
uint32_t tablesSize = tables.totalSize();
if ( tablesSize < (totalUnoptLinkeditsSize-newLinkeditUnalignedSize) ) {
tables.copyTo((uint8_t*)cache.buffer().get()+newLinkeditEnd);
newLinkeditEnd += tablesSize;
uint64_t accelInfoAddr = align(linkeditStartAddr + newLinkeditUnalignedSize, 14);
cache.setAcceleratorInfoRange(accelInfoAddr, tablesSize);
}
else {
warning("not enough room to add dyld accelerator tables");
}
}
cache.setLinkeditsMappingEndFileOffset(newLinkeditEnd);
uint64_t newFileSize = newLinkeditEnd;
if ( dontMapLocalSymbols ) {
typedef typename P::E E;
uint32_t localSymbolsOffset = (uint32_t)align(newFileSize, 14);
uint32_t spaceAtEnd = linkeditEndOffset - (uint32_t)newLinkeditEnd;
dyldCacheLocalSymbolsInfo<E>* infoHeader = (dyldCacheLocalSymbolsInfo<E>*)((uint8_t*)cache.buffer().get()+localSymbolsOffset);
const uint32_t entriesOffset = sizeof(dyldCacheLocalSymbolsInfo<E>);
const uint32_t entriesCount = (uint32_t)localSymbolInfos.size();
const uint32_t nlistOffset = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyldCacheLocalSymbolEntry<E>), 4); const uint32_t nlistCount = (uint32_t)unmappedLocalSymbols.size();
const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist<P>);
dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(((uint8_t*)infoHeader)+entriesOffset);
for (int i=0; i < entriesCount; ++i) {
entries[i].set_dylibOffset(localSymbolInfos[i].dylibOffset);
entries[i].set_nlistStartIndex(localSymbolInfos[i].nlistStartIndex);
entries[i].set_nlistCount(localSymbolInfos[i].nlistCount);
}
macho_nlist<P>* newLocalsSymbolTable = (macho_nlist<P>*)(((uint8_t*)infoHeader)+nlistOffset);
::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
const uint32_t stringsSize = localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
const uint32_t localsRegionSize = (uint32_t)align(stringsOffset+stringsSize, 14);
if ( localsRegionSize > spaceAtEnd )
terminate("not enough room to store local symbols");
infoHeader->set_nlistOffset(nlistOffset);
infoHeader->set_nlistCount(nlistCount);
infoHeader->set_stringsOffset(stringsOffset);
infoHeader->set_stringsSize(stringsSize);
infoHeader->set_entriesOffset(entriesOffset);
infoHeader->set_entriesCount(entriesCount);
newFileSize += localsRegionSize;
verboseLog("Unmapped local symbol info: %uMB", localsRegionSize/(1024*1024));
cache.setUnmappedLocalsRange(localSymbolsOffset, localsRegionSize);
}
for (LinkeditOptimizer<P>* op : optimizers) {
op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset,
sharedSymbolTableStartOffset, sharedSymbolTableCount,
sharedSymbolStringsOffset, sharedSymbolStringsSize);
}
return newFileSize;
}
}
template <typename P>
void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets)
{
std::vector<LinkeditOptimizer<P>*> optimizers;
forEachImage([&](const void* mh, const char*, time_t, ino_t, const std::vector<MachOProxy::Segment>&) {
optimizers.push_back(new LinkeditOptimizer<P>(_buffer.get(), (macho_header<P>*)mh));
});
for (uint64_t poolOffset : branchPoolOffsets) {
macho_header<P>* mh = (macho_header<P>*)((char*)_buffer.get() + poolOffset);
optimizers.push_back(new LinkeditOptimizer<P>(_buffer.get(), mh));
}
_fileSize = mergeLinkedits(*this, dontMapLocalSymbols, addAcceleratorTables, optimizers);
for (LinkeditOptimizer<P>* op : optimizers)
delete op;
}
void SharedCache::optimizeLinkedit(bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector<uint64_t>& branchPoolOffsets)
{
switch ( _arch.arch ) {
case CPU_TYPE_ARM:
case CPU_TYPE_I386:
optimizeLinkedit<Pointer32<LittleEndian>>(dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets);
break;
case CPU_TYPE_X86_64:
case CPU_TYPE_ARM64:
optimizeLinkedit<Pointer64<LittleEndian>>(dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets);
break;
default:
terminate("unsupported arch 0x%08X", _arch.arch);
}
}