#include "mega-dylib-utils.h"
#include "MachOFileAbstraction.hpp"
#include "Trie.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"
#if !NEW_CACHE_FILE_FORMAT
#include "CacheFileAbstraction.hpp"
#endif
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
#endif
namespace {
template <typename P>
class BindInfo {
public:
BindInfo(void* cacheBuffer, macho_header<P>* mh);
void setReExports(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo);
void setDependentDylibs(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo);
void bind(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
static void bindAllImagesInCache(void* cacheBuffer, const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR);
void addExportsToGlobalMap(std::unordered_map<std::string, BindInfo<P>*>& reverseMap);
private:
typedef typename P::uint_t pint_t;
typedef typename P::E E;
struct SymbolInfo {
SymbolInfo() { }
pint_t address = 0;
bool isResolver = false;
bool isAbsolute = false;
bool isSymbolReExport = false;
bool isThreadLocal = false;
int reExportDylibIndex = 0;
std::string reExportName;
};
void bindImmediates(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
void bindLazyPointers(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
void bindLocation(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport,
const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR);
bool findExportedSymbolAddress(const char* symbolName, const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo,
pint_t* address, BindInfo<P>** foundIn, bool* isResolverSymbol, bool* isAbsolute);
pint_t findBlessedLazyPointerFor(const std::string& resolverSymbolName);
void switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr);
void switchArmStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
void switchArm64StubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
typedef std::unordered_map<std::string, std::unordered_set<BindInfo<P>*>> ResolverClientsMap;
typedef std::unordered_map<std::string, pint_t> ResolverToBlessedLazyPointerMap;
void* _cacheBuffer;
macho_header<P>* _mh;
const uint8_t* _linkeditBias;
const char* _installName;
const macho_symtab_command<P>* _symTabCmd;
const macho_dysymtab_command<P>* _dynSymTabCmd;
const macho_dyld_info_command<P>* _dyldInfo;
std::vector<std::string> _dependentPaths;
std::vector<uint64_t> _segSizes;
std::vector<uint64_t> _segCacheOffsets;
std::vector<const macho_segment_command<P>*>_segCmds;
std::unordered_map<std::string, SymbolInfo> _exports;
std::vector<std::string> _reExportedDylibNames;
std::vector<BindInfo<P>*> _reExportedDylibs;
std::vector<BindInfo<P>*> _dependentDylibs;
pint_t _baseAddress;
ResolverClientsMap _resolverClients;
ResolverToBlessedLazyPointerMap _resolverBlessedMap;
};
template <typename P>
BindInfo<P>::BindInfo(void* cacheBuffer, macho_header<P>* mh)
: _cacheBuffer(cacheBuffer), _mh(mh), _linkeditBias((uint8_t*)cacheBuffer), _symTabCmd(nullptr), _dynSymTabCmd(nullptr), _dyldInfo(nullptr), _baseAddress(0)
{
macho_segment_command<P>* segCmd;
macho_dylib_command<P>* dylibCmd;
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
const uint32_t cmd_count = mh->ncmds();
unsigned segIndex = 0;
const macho_load_command<P>* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd()) {
case LC_ID_DYLIB:
dylibCmd = (macho_dylib_command<P>*)cmd;
_installName = dylibCmd->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;
break;
case LC_REEXPORT_DYLIB:
dylibCmd = (macho_dylib_command<P>*)cmd;
_dependentPaths.push_back(dylibCmd->name());
_reExportedDylibNames.push_back(dylibCmd->name());
break;
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_LOAD_UPWARD_DYLIB:
dylibCmd = (macho_dylib_command<P>*)cmd;
_dependentPaths.push_back(dylibCmd->name());
break;
case macho_segment_command<P>::CMD:
segCmd = (macho_segment_command<P>*)cmd;
_segCmds.push_back(segCmd);
_segSizes.push_back(segCmd->vmsize());
_segCacheOffsets.push_back(segCmd->fileoff());
if ( segIndex == 0 )
_baseAddress = (pint_t)segCmd->vmaddr();
++segIndex;
break;
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
if ( _dyldInfo->export_size() == 0 )
return;
std::vector<ExportInfoTrie::Entry> exports;
const uint8_t* exportsStart = &_linkeditBias[_dyldInfo->export_off()];
const uint8_t* exportsEnd = &exportsStart[_dyldInfo->export_size()];
if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) {
terminate("malformed exports trie in %s", _installName);
}
for(const ExportInfoTrie::Entry& entry : exports) {
_exports[entry.name].address = (pint_t)entry.info.address + _baseAddress;
switch ( entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
_exports[entry.name].isResolver = true;
}
if ( entry.info.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
SymbolInfo& info = _exports[entry.name];
info.isSymbolReExport = true;
info.reExportDylibIndex = (int)entry.info.other;
if ( !entry.info.importName.empty())
info.reExportName = entry.info.importName;
}
break;
case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
_exports[entry.name].isThreadLocal = true;
break;
case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
_exports[entry.name].isAbsolute = true;
_exports[entry.name].address = (pint_t)entry.info.address;
break;
default:
terminate("non-regular symbol binding not supported for %s in %s", entry.name.c_str(), _installName);
break;
}
}
}
template <typename P>
void BindInfo<P>::bind(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
{
bindImmediates(dylibPathToBindInfo, pointersForASLR);
bindLazyPointers(dylibPathToBindInfo, pointersForASLR);
}
template <typename P>
void BindInfo<P>::setReExports(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo)
{
for (const std::string& depName : _reExportedDylibNames) {
auto pos = dylibPathToBindInfo.find(depName);
if ( pos == dylibPathToBindInfo.end() ) {
terminate("can't find re-exported dylib '%s' needed by '%s'", depName.c_str(), _installName);
}
_reExportedDylibs.push_back(pos->second);
}
}
template <typename P>
void BindInfo<P>::setDependentDylibs(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo)
{
for (const std::string& depName : _dependentPaths) {
auto pos = dylibPathToBindInfo.find(depName);
if ( pos == dylibPathToBindInfo.end() ) {
terminate("can't find dependent dylib '%s' needed by '%s'", depName.c_str(), _installName);
}
_dependentDylibs.push_back(pos->second);
}
}
template <typename P>
void BindInfo<P>::bindImmediates(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
{
const uint8_t* p = &_linkeditBias[_dyldInfo->bind_off()];
const uint8_t* end = &p[_dyldInfo->bind_size()];
uint8_t type = 0;
uint64_t segmentOffset = 0;
uint8_t segmentIndex = 0;
const char* symbolName = NULL;
int libraryOrdinal = 0;
int64_t addend = 0;
uint64_t count;
uint64_t skip;
bool weakImport = false;
bool done = false;
while ( !done && (p < end) ) {
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
uint8_t opcode = *p & BIND_OPCODE_MASK;
++p;
switch (opcode) {
case BIND_OPCODE_DONE:
done = true;
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
libraryOrdinal = immediate;
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
libraryOrdinal = (int)read_uleb128(p, end);
break;
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
if ( immediate == 0 )
libraryOrdinal = 0;
else {
int8_t signExtended = BIND_OPCODE_MASK | immediate;
libraryOrdinal = signExtended;
}
break;
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
symbolName = (char*)p;
while (*p != '\0')
++p;
++p;
break;
case BIND_OPCODE_SET_TYPE_IMM:
type = immediate;
break;
case BIND_OPCODE_SET_ADDEND_SLEB:
addend = read_sleb128(p, end);
break;
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segmentIndex = immediate;
segmentOffset = read_uleb128(p, end);
break;
case BIND_OPCODE_ADD_ADDR_ULEB:
segmentOffset += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
segmentOffset += sizeof(pint_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
segmentOffset += read_uleb128(p, end) + sizeof(pint_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
segmentOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
break;
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, weakImport, dylibPathToBindInfo, pointersForASLR);
segmentOffset += skip + sizeof(pint_t);
}
break;
default:
terminate("bad bind opcode 0x%02X in %s", *p, _installName);
}
}
}
template <typename P>
void BindInfo<P>::bindLazyPointers(const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
{
const uint8_t* p = &_linkeditBias[_dyldInfo->lazy_bind_off()];
const uint8_t* end = &p[_dyldInfo->lazy_bind_size()];
uint8_t type = BIND_TYPE_POINTER;
uint64_t segmentOffset = 0;
uint8_t segmentIndex = 0;
const char* symbolName = NULL;
int libraryOrdinal = 0;
int64_t addend = 0;
bool weakImport = false;
while ( p < end ) {
uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
uint8_t opcode = *p & BIND_OPCODE_MASK;
++p;
switch (opcode) {
case BIND_OPCODE_DONE:
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
libraryOrdinal = immediate;
break;
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
libraryOrdinal = (int)read_uleb128(p, end);
break;
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
if ( immediate == 0 )
libraryOrdinal = 0;
else {
int8_t signExtended = BIND_OPCODE_MASK | immediate;
libraryOrdinal = signExtended;
}
break;
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 );
symbolName = (char*)p;
while (*p != '\0')
++p;
++p;
break;
case BIND_OPCODE_SET_ADDEND_SLEB:
addend = read_sleb128(p, end);
break;
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segmentIndex = immediate;
segmentOffset = read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
bindLocation(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, weakImport, dylibPathToBindInfo, pointersForASLR);
segmentOffset += sizeof(pint_t);
break;
case BIND_OPCODE_SET_TYPE_IMM:
case BIND_OPCODE_ADD_ADDR_ULEB:
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
default:
terminate("bad lazy bind opcode 0x%02X in %s", opcode, _installName);
}
}
}
template <typename P>
void BindInfo<P>::bindLocation(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport,
const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo, std::vector<void*>& pointersForASLR)
{
if ( segmentIndex > _segSizes.size() )
terminate("bad segment index in bind info in %s", _installName);
if ( segmentOffset > _segSizes[segmentIndex] )
terminate("bad segment offset in bind info in %s", _installName);
BindInfo<P>* targetBinder = nullptr;
std::string depName;
switch ( libraryOrdinal ) {
case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
terminate("dynamic lookup linkage not allowed in dyld shared cache in %s", _installName);
break;
case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
terminate("linkage to main executable not allowed in dyld shared cache in %s", _installName);
break;
case BIND_SPECIAL_DYLIB_SELF:
targetBinder = this;
break;
default:
if ( libraryOrdinal < 0 )
terminate("bad mach-o binary, special library ordinal not allowd in dyld shared cache in %s", _installName);
if ( (unsigned)libraryOrdinal > _dependentPaths.size() )
terminate("bad mach-o binary, library ordinal too big in %s", _installName);
depName = _dependentPaths[libraryOrdinal-1];
auto pos = dylibPathToBindInfo.find(depName);
if ( pos != dylibPathToBindInfo.end() )
targetBinder = pos->second;
break;
}
pint_t targetSymbolAddress;
bool isResolverSymbol = false;
bool isAbsolute = false;
BindInfo<P>* foundIn;
if ( weakImport && (targetBinder == nullptr) ) {
targetSymbolAddress = 0;
foundIn = nullptr;
}
else {
if (targetBinder == nullptr)
terminate("could not bind symbol '%s' used in '%s' because installname '%s' not found", symbolName, _installName, depName.c_str());
if ( ! targetBinder->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) )
terminate("could not bind symbol '%s' used in: %s expected in: %s", symbolName, _installName, targetBinder->_installName);
}
if ( lazyPointer && isResolverSymbol ) {
pint_t lpVMAddr = targetBinder->findBlessedLazyPointerFor(symbolName);
this->switchStubToUseSharedLazyPointer(symbolName, lpVMAddr);
return;
}
uint8_t* mappedAddr = (uint8_t*)_cacheBuffer + _segCacheOffsets[segmentIndex] + segmentOffset;
pint_t* mappedAddrP = (pint_t*)mappedAddr;
pint_t newValue = (pint_t)(targetSymbolAddress + addend);
switch ( type ) {
case BIND_TYPE_POINTER:
if ( P::getP(*mappedAddrP) != newValue )
P::setP(*mappedAddrP, newValue);
break;
case BIND_TYPE_TEXT_ABSOLUTE32:
case BIND_TYPE_TEXT_PCREL32:
terminate("text relocs not supported for shared cache binding in %s", _installName);
break;
default:
terminate("bad bind type (%d) in %s", type, _installName);
}
if ( !isAbsolute )
pointersForASLR.push_back(mappedAddr);
}
template <typename P>
bool BindInfo<P>::findExportedSymbolAddress(const char* symbolName, const std::unordered_map<std::string, BindInfo<P>*>& dylibPathToBindInfo,
pint_t* address, BindInfo<P>** foundIn, bool* isResolverSymbol, bool* isAbsolute)
{
auto pos = _exports.find(symbolName);
if ( pos != _exports.end() ) {
if ( pos->second.isSymbolReExport ) {
const char* importName = symbolName;
if ( !pos->second.reExportName.empty() )
importName = pos->second.reExportName.c_str();
std::string& depPath = _dependentPaths[pos->second.reExportDylibIndex-1];
auto pos2 = dylibPathToBindInfo.find(depPath);
if ( pos2 != dylibPathToBindInfo.end() ) {
BindInfo<P>* reExportFrom = pos2->second;
return reExportFrom->findExportedSymbolAddress(importName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute);
}
else {
verboseLog("findExportedSymbolAddress(%s) => ???\n", symbolName);
}
}
*address = pos->second.address;
*foundIn = this;
*isResolverSymbol = pos->second.isResolver;
*isAbsolute = pos->second.isAbsolute;
return true;
}
for (BindInfo<P>* dep : _reExportedDylibs) {
if ( dep->findExportedSymbolAddress(symbolName, dylibPathToBindInfo, address, foundIn, isResolverSymbol, isAbsolute) )
return true;
}
return false;
}
template <typename P>
void BindInfo<P>::addExportsToGlobalMap(std::unordered_map<std::string, BindInfo<P>*>& reverseMap)
{
for (const auto& expEntry : _exports) {
const std::string& symName = expEntry.first;
auto pos = reverseMap.find(symName);
if ( pos == reverseMap.end() ) {
reverseMap[symName] = this;
}
else {
BindInfo<P>* other = pos->second;
if ( expEntry.second.isSymbolReExport )
continue;
if ( other->_exports[symName].isSymbolReExport )
continue;
}
}
}
template <typename P>
typename P::uint_t BindInfo<P>::findBlessedLazyPointerFor(const std::string& resolverSymbolName)
{
static const bool log = false;
auto pos1 = _resolverBlessedMap.find(resolverSymbolName);
if ( pos1 != _resolverBlessedMap.end() ) {
return pos1->second;
}
bool thisDylibImplementsResolver = false;
auto pos = _exports.find(resolverSymbolName);
if ( pos != _exports.end() ) {
const SymbolInfo& info = pos->second;
if ( info.isSymbolReExport ) {
std::string reImportName = resolverSymbolName;
if ( !info.reExportName.empty() )
reImportName = info.reExportName;
if ( info.reExportDylibIndex > _dependentDylibs.size() ) {
warning("dylib index for re-exported symbol %s too large (%d) in %s", resolverSymbolName.c_str(), info.reExportDylibIndex, _installName);
}
else {
BindInfo<P>* reExportedFrom = _dependentDylibs[info.reExportDylibIndex-1];
if ( log ) verboseLog( "following re-export of %s in %s, to %s in %s", resolverSymbolName.c_str(), _installName, reImportName.c_str(), reExportedFrom->_installName);
pint_t lp = reExportedFrom->findBlessedLazyPointerFor(reImportName);
if ( lp != 0 ) {
_resolverBlessedMap[resolverSymbolName] = lp;
return lp;
}
}
}
if ( info.isResolver )
thisDylibImplementsResolver = true;
}
if ( thisDylibImplementsResolver ) {
const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
for (const macho_segment_command<P>* seg : _segCmds) {
const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)seg + sizeof(macho_segment_command<P>));
const macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
uint8_t sectionType = sect->flags() & SECTION_TYPE;
if ( sectionType == S_LAZY_SYMBOL_POINTERS) {
uint32_t elementCount = (uint32_t)(sect->size() / sizeof(pint_t));
const uint32_t indirectTableOffset = sect->reserved1();
pint_t vmlocation = (pint_t)sect->addr();
for (uint32_t j=0; j < elementCount; ++j, vmlocation += sizeof(pint_t)) {
uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
switch ( symbolIndex ) {
case INDIRECT_SYMBOL_ABS:
case INDIRECT_SYMBOL_LOCAL:
break;
default:
const macho_nlist<P>* aSymbol = &symbolTable[symbolIndex];
const char* aName = &symbolStringPool[aSymbol->n_strx()];
if ( resolverSymbolName == aName) {
if ( log ) verboseLog("found shared lazy pointer at 0x%llX for %s in %s in %s", (uint64_t)vmlocation, aName, sect->sectname(), _installName);
_resolverBlessedMap[resolverSymbolName] = vmlocation;
return vmlocation;
}
break;
}
}
}
}
}
}
if ( log ) verboseLog( "not found shared lazy pointer for %s in %s, checking re-export dylibs", resolverSymbolName.c_str(), _installName);
for (BindInfo<P>* reExportedDylib : _reExportedDylibs ) {
pint_t result = reExportedDylib->findBlessedLazyPointerFor(resolverSymbolName);
if ( result != 0 ) {
_resolverBlessedMap[resolverSymbolName] = result;
return result;
}
}
if ( log ) verboseLog( "NOT found shared lazy pointer for %s in %s", resolverSymbolName.c_str(), _installName);
return 0;
}
template <typename P>
void BindInfo<P>::switchStubToUseSharedLazyPointer(const std::string& resolverSymbolName, pint_t lpVMAddr)
{
const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
const char* symbolStringPool = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
for (const macho_segment_command<P>* seg : _segCmds) {
macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) {
pint_t stubsVMStart = (pint_t)sect->addr();
uint8_t* stubsMappingStart = ((uint8_t*)_cacheBuffer) + sect->offset();
const uint32_t indirectTableOffset = sect->reserved1();
const uint32_t stubSize = sect->reserved2();
uint32_t elementCount = (uint32_t)(sect->size() / stubSize);
pint_t stubVMAddr = stubsVMStart;
uint8_t* stubMappedAddr = stubsMappingStart;
for (uint32_t j=0; j < elementCount; ++j, stubMappedAddr += stubSize, stubVMAddr += stubSize) {
uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
switch ( symbolIndex ) {
case INDIRECT_SYMBOL_ABS:
case INDIRECT_SYMBOL_LOCAL:
break;
default:
{
const macho_nlist<P>* aSymbol = &symbolTable[symbolIndex];
const char* stubName = &symbolStringPool[aSymbol->n_strx()];
if ( resolverSymbolName == stubName ) {
switch (_mh->cputype()) {
case CPU_TYPE_ARM:
switchArmStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr);
break;
default:
break;
}
}
}
break;
}
}
}
}
}
}
template <typename P>
void BindInfo<P>::switchArmStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
{
if ( stubSize != 16 ) {
warning("could not optimize ARM stub to resolver function in %s because it is wrong size\n", _installName);
return;
}
uint32_t* instructions = (uint32_t*)stubMappedAddress;
if ( (E::get32(instructions[0]) != 0xe59fc004)
|| (E::get32(instructions[1]) != 0xe08fc00c)
|| (E::get32(instructions[2]) != 0xe59cf000)
) {
warning("could not optimize ARM stub to resolver function in %s because instructions are not as expected", _installName);
return;
}
uint32_t betterOffset = (uint32_t)(lpVMAddr - (stubVMAddress + 12));
E::set32(instructions[3], betterOffset);
}
template <typename P>
void BindInfo<P>::bindAllImagesInCache(void* cacheBuffer, const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR)
{
std::unordered_map<macho_header<P>*, BindInfo<P>*> headersToBindInfo;
std::unordered_map<std::string, BindInfo<P>*> dylibPathToBindInfo;
for (const auto& entry: dylibPathToMachHeader) {
macho_header<P>* mh = (macho_header<P>*)entry.second;
if ( headersToBindInfo.count(mh) == 0 )
headersToBindInfo[mh] = new BindInfo<P>(cacheBuffer, mh);
dylibPathToBindInfo[entry.first] = headersToBindInfo[mh];
}
for (const auto& entry: headersToBindInfo) {
entry.second->setDependentDylibs(dylibPathToBindInfo);
entry.second->setReExports(dylibPathToBindInfo);
}
for (const auto& entry: headersToBindInfo) {
entry.second->bind(dylibPathToBindInfo, pointersForASLR);
}
std::unordered_map<std::string, BindInfo<P>*> reverseMap;
for (const auto& entry: headersToBindInfo) {
entry.second->addExportsToGlobalMap(reverseMap);
}
for (const auto& entry: headersToBindInfo) {
delete entry.second;
}
}
}
void SharedCache::bindAllImagesInCache(const std::unordered_map<std::string, void*>& dylibPathToMachHeader, std::vector<void*>& pointersForASLR)
{
switch ( _arch.arch ) {
case CPU_TYPE_ARM:
case CPU_TYPE_I386:
BindInfo<Pointer32<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibPathToMachHeader, pointersForASLR);
break;
case CPU_TYPE_X86_64:
case CPU_TYPE_ARM64:
BindInfo<Pointer64<LittleEndian>>::bindAllImagesInCache(_buffer.get(), dylibPathToMachHeader, pointersForASLR);
break;
default:
terminate("unsupported arch 0x%08X", _arch.arch);
}
}