AdjustDylibSegments.cpp [plain text]
#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 <string>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include "CacheBuilder.h"
#include "Diagnostics.h"
#include "DyldSharedCache.h"
#include "Trie.hpp"
#include "MachOFileAbstraction.hpp"
#include "MachOLoaded.h"
#include "MachOAnalyzer.h"
#include "mach-o/fixup-chains.h"
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
#endif
namespace {
template <typename P>
class Adjustor {
public:
Adjustor(uint64_t cacheBaseAddress, dyld3::MachOAnalyzer* mh, const char* dylibID,
const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag);
void adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker,
CacheBuilder::LOH_Tracker* lohTracker,
const CacheBuilder::CacheCoalescedText* coalescedText,
const CacheBuilder::DylibTextCoalescer& textCoalescer);
private:
void adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker,
CacheBuilder::LOH_Tracker* lohTracker,
const CacheBuilder::CacheCoalescedText* coalescedText,
const CacheBuilder::DylibTextCoalescer& textCoalescer);
void adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide,
uint64_t imageStartAddress, uint64_t imageEndAddress, bool convertRebaseChains,
CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress);
void adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker);
void adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker);
void slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker);
void adjustSymbolTable();
void adjustChainedFixups(const CacheBuilder::DylibTextCoalescer& textCoalescer);
void adjustExternalRelocations();
void adjustExportsTrie(std::vector<uint8_t>& newTrieBytes);
void rebuildLinkEdit();
void adjustCode();
void adjustInstruction(uint8_t kind, uint8_t* textLoc, uint64_t codeToDataDelta);
void rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCoalescer& textCoalescer);
uint64_t slideForOrigAddress(uint64_t addr);
void convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide);
void convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker,
uint64_t targetSlide, bool convertRebaseChains);
typedef typename P::uint_t pint_t;
typedef typename P::E E;
uint64_t _cacheBaseAddress = 0;
dyld3::MachOAnalyzer* _mh;
Diagnostics& _diagnostics;
const uint8_t* _linkeditBias = nullptr;
unsigned _linkeditSegIndex = 0;
bool _maskPointers = false;
bool _splitSegInfoV2 = false;
const char* _dylibID = nullptr;
symtab_command* _symTabCmd = nullptr;
dysymtab_command* _dynSymTabCmd = nullptr;
dyld_info_command* _dyldInfo = nullptr;
linkedit_data_command* _splitSegInfoCmd = nullptr;
linkedit_data_command* _functionStartsCmd = nullptr;
linkedit_data_command* _dataInCodeCmd = nullptr;
linkedit_data_command* _exportTrieCmd = nullptr;
linkedit_data_command* _chainedFixupsCmd = nullptr;
uint16_t _chainedFixupsFormat = 0;
std::vector<uint64_t> _segOrigStartAddresses;
std::vector<uint64_t> _segSizes;
std::vector<uint64_t> _segSlides;
std::vector<macho_segment_command<P>*> _segCmds;
const std::vector<CacheBuilder::SegmentMappingInfo>& _mappingInfo;
};
template <typename P>
Adjustor<P>::Adjustor(uint64_t cacheBaseAddress, dyld3::MachOAnalyzer* mh, const char* dylibID,
const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag)
: _cacheBaseAddress(cacheBaseAddress), _mh(mh), _diagnostics(diag), _dylibID(dylibID), _mappingInfo(mappingInfo)
{
assert((_mh->magic == MH_MAGIC) || (_mh->magic == MH_MAGIC_64));
__block unsigned segIndex = 0;
mh->forEachLoadCommand(diag, ^(const load_command *cmd, bool &stop) {
switch ( cmd->cmd ) {
case LC_SYMTAB:
_symTabCmd = (symtab_command*)cmd;
break;
case LC_DYSYMTAB:
_dynSymTabCmd = (dysymtab_command*)cmd;
break;
case LC_DYLD_INFO:
case LC_DYLD_INFO_ONLY:
_dyldInfo = (dyld_info_command*)cmd;
break;
case LC_SEGMENT_SPLIT_INFO:
_splitSegInfoCmd = (linkedit_data_command*)cmd;
break;
case LC_FUNCTION_STARTS:
_functionStartsCmd = (linkedit_data_command*)cmd;
break;
case LC_DATA_IN_CODE:
_dataInCodeCmd = (linkedit_data_command*)cmd;
break;
case LC_DYLD_CHAINED_FIXUPS:
_chainedFixupsCmd = (linkedit_data_command*)cmd;
_chainedFixupsFormat = dyld3::MachOAnalyzer::chainedPointerFormat((dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff]);
break;
case LC_DYLD_EXPORTS_TRIE:
_exportTrieCmd = (linkedit_data_command*)cmd;
break;
case macho_segment_command<P>::CMD:
macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
_segCmds.push_back(segCmd);
_segOrigStartAddresses.push_back(segCmd->vmaddr());
_segSizes.push_back(segCmd->vmsize());
_segSlides.push_back(_mappingInfo[segIndex].dstCacheUnslidAddress - segCmd->vmaddr());
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
_linkeditBias = (uint8_t*)_mappingInfo[segIndex].dstSegment - segCmd->fileoff();
_linkeditSegIndex = segIndex;
}
++segIndex;
break;
}
});
_maskPointers = (mh->cputype == CPU_TYPE_ARM64) || (mh->cputype == CPU_TYPE_ARM64_32);
if ( _splitSegInfoCmd != NULL ) {
const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff];
_splitSegInfoV2 = (*infoStart == DYLD_CACHE_ADJ_V2_FORMAT);
}
else {
bool canHaveMissingSplitSeg = false;
#if BUILDING_APP_CACHE_UTIL
if ( mh->isKextBundle() ) {
if ( mh->isArch("x86_64") || mh->isArch("x86_64h") )
canHaveMissingSplitSeg = true;
}
#endif
if ( !canHaveMissingSplitSeg )
_diagnostics.error("missing LC_SEGMENT_SPLIT_INFO in %s", _dylibID);
}
if ( (_chainedFixupsCmd == nullptr) && mh->isArch("arm64e") ) {
_chainedFixupsFormat = DYLD_CHAINED_PTR_ARM64E;
}
}
template <typename P>
void Adjustor<P>::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker,
CacheBuilder::LOH_Tracker* lohTracker,
const CacheBuilder::CacheCoalescedText* coalescedText,
const CacheBuilder::DylibTextCoalescer& textCoalescer)
{
if ( _diagnostics.hasError() )
return;
if ( _splitSegInfoV2 ) {
adjustReferencesUsingInfoV2(aslrTracker, lohTracker, coalescedText, textCoalescer);
adjustChainedFixups(textCoalescer);
}
else if ( _chainedFixupsCmd != nullptr ) {
adjustChainedFixups(textCoalescer);
adjustRebaseChains(aslrTracker);
adjustCode();
}
else {
adjustDataPointers(aslrTracker);
adjustCode();
}
if ( _diagnostics.hasError() )
return;
adjustSymbolTable();
if ( _diagnostics.hasError() )
return;
adjustExternalRelocations();
if ( _diagnostics.hasError() )
return;
rebuildLinkEditAndLoadCommands(textCoalescer);
#if DEBUG
Diagnostics diag;
_mh->validateDyldCacheDylib(diag, _dylibID);
if ( diag.hasError() ) {
fprintf(stderr, "%s\n", diag.errorMessage().c_str());
}
#endif
}
template <typename P>
uint64_t Adjustor<P>::slideForOrigAddress(uint64_t addr)
{
for (unsigned i=0; i < _segOrigStartAddresses.size(); ++i) {
if ( (_segOrigStartAddresses[i] <= addr) && (addr < (_segOrigStartAddresses[i]+_segCmds[i]->vmsize())) )
return _segSlides[i];
}
if ( _maskPointers && (addr & 0xF000000000000000) ) {
return slideForOrigAddress(addr & 0x0FFFFFFFFFFFFFFF);
}
_diagnostics.error("slide not known for dylib address 0x%llX in %s", addr, _dylibID);
return 0;
}
template <typename P>
void Adjustor<P>::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCoalescer& textCoalescer)
{
std::vector<uint8_t> newTrieBytes;
adjustExportsTrie(newTrieBytes);
uint32_t chainedFixupsOffset = 0;
uint32_t chainedFixupsSize = _chainedFixupsCmd ? _chainedFixupsCmd->datasize : 0;
uint32_t bindOffset = chainedFixupsOffset + chainedFixupsSize;
uint32_t bindSize = _dyldInfo ? _dyldInfo->bind_size : 0;
uint32_t weakBindOffset = bindOffset + bindSize;
uint32_t weakBindSize = _dyldInfo ? _dyldInfo->weak_bind_size : 0;
uint32_t lazyBindOffset = weakBindOffset + weakBindSize;
uint32_t lazyBindSize = _dyldInfo ? _dyldInfo->lazy_bind_size : 0;
uint32_t exportOffset = lazyBindOffset + lazyBindSize;
uint32_t exportSize = (uint32_t)newTrieBytes.size();
uint32_t splitSegInfoOffset = exportOffset + exportSize;
uint32_t splitSegInfosSize = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize : 0);
uint32_t funcStartsOffset = splitSegInfoOffset + splitSegInfosSize;
uint32_t funcStartsSize = (_functionStartsCmd ? _functionStartsCmd->datasize : 0);
uint32_t dataInCodeOffset = funcStartsOffset + funcStartsSize;
uint32_t dataInCodeSize = (_dataInCodeCmd ? _dataInCodeCmd->datasize : 0);
uint32_t symbolTableOffset = dataInCodeOffset + dataInCodeSize;
uint32_t symbolTableSize = _symTabCmd->nsyms * sizeof(macho_nlist<P>);
uint32_t indirectTableOffset = symbolTableOffset + symbolTableSize;
uint32_t indirectTableSize = _dynSymTabCmd ? (_dynSymTabCmd->nindirectsyms * sizeof(uint32_t)) : 0;
uint32_t externalRelocOffset = indirectTableOffset + indirectTableSize;
uint32_t externalRelocSize = _dynSymTabCmd ? (_dynSymTabCmd->nextrel * sizeof(relocation_info)) : 0;
uint32_t symbolStringsOffset = externalRelocOffset + externalRelocSize;
uint32_t symbolStringsSize = _symTabCmd->strsize;
uint32_t newLinkEditSize = symbolStringsOffset + symbolStringsSize;
size_t linkeditBufferSize = align(_segCmds[_linkeditSegIndex]->vmsize(), 12);
if ( linkeditBufferSize < newLinkEditSize ) {
_diagnostics.error("LINKEDIT overflow in %s", _dylibID);
return;
}
uint8_t* newLinkeditBufer = (uint8_t*)::calloc(linkeditBufferSize, 1);
if ( chainedFixupsSize )
memcpy(&newLinkeditBufer[chainedFixupsOffset], &_linkeditBias[_chainedFixupsCmd->dataoff], chainedFixupsSize);
if ( bindSize )
memcpy(&newLinkeditBufer[bindOffset], &_linkeditBias[_dyldInfo->bind_off], bindSize);
if ( lazyBindSize )
memcpy(&newLinkeditBufer[lazyBindOffset], &_linkeditBias[_dyldInfo->lazy_bind_off], lazyBindSize);
if ( weakBindSize )
memcpy(&newLinkeditBufer[weakBindOffset], &_linkeditBias[_dyldInfo->weak_bind_off], weakBindSize);
if ( exportSize )
memcpy(&newLinkeditBufer[exportOffset], &newTrieBytes[0], exportSize);
if ( splitSegInfosSize )
memcpy(&newLinkeditBufer[splitSegInfoOffset], &_linkeditBias[_splitSegInfoCmd->dataoff], splitSegInfosSize);
if ( funcStartsSize )
memcpy(&newLinkeditBufer[funcStartsOffset], &_linkeditBias[_functionStartsCmd->dataoff], funcStartsSize);
if ( dataInCodeSize )
memcpy(&newLinkeditBufer[dataInCodeOffset], &_linkeditBias[_dataInCodeCmd->dataoff], dataInCodeSize);
if ( symbolTableSize )
memcpy(&newLinkeditBufer[symbolTableOffset], &_linkeditBias[_symTabCmd->symoff], symbolTableSize);
if ( indirectTableSize )
memcpy(&newLinkeditBufer[indirectTableOffset], &_linkeditBias[_dynSymTabCmd->indirectsymoff], indirectTableSize);
if ( externalRelocSize )
memcpy(&newLinkeditBufer[externalRelocOffset], &_linkeditBias[_dynSymTabCmd->extreloff], externalRelocSize);
if ( symbolStringsSize )
memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff], symbolStringsSize);
memcpy(_mappingInfo[_linkeditSegIndex].dstSegment, newLinkeditBufer, newLinkEditSize);
::bzero(((uint8_t*)_mappingInfo[_linkeditSegIndex].dstSegment)+newLinkEditSize, linkeditBufferSize-newLinkEditSize);
::free(newLinkeditBufer);
uint32_t linkeditStartOffset = (uint32_t)_mappingInfo[_linkeditSegIndex].dstCacheFileOffset;
__block unsigned segIndex = 0;
_mh->forEachLoadCommand(_diagnostics, ^(const load_command *cmd, bool &stop) {
symtab_command* symTabCmd;
dysymtab_command* dynSymTabCmd;
dyld_info_command* dyldInfo;
linkedit_data_command* functionStartsCmd;
linkedit_data_command* dataInCodeCmd;
linkedit_data_command* chainedFixupsCmd;
linkedit_data_command* exportTrieCmd;
linkedit_data_command* splitSegInfoCmd;
macho_segment_command<P>* segCmd;
macho_routines_command<P>* routinesCmd;
dylib_command* dylibIDCmd;
int32_t segFileOffsetDelta;
switch ( cmd->cmd ) {
case LC_ID_DYLIB:
dylibIDCmd = (dylib_command*)cmd;
dylibIDCmd->dylib.timestamp = 2; break;
case LC_SYMTAB:
symTabCmd = (symtab_command*)cmd;
symTabCmd->symoff = linkeditStartOffset + symbolTableOffset;
symTabCmd->stroff = linkeditStartOffset + symbolStringsOffset;
break;
case LC_DYSYMTAB:
dynSymTabCmd = (dysymtab_command*)cmd;
dynSymTabCmd->indirectsymoff = linkeditStartOffset + indirectTableOffset;
dynSymTabCmd->locreloff = 0;
dynSymTabCmd->nlocrel = 0 ;
dynSymTabCmd->extreloff = linkeditStartOffset + externalRelocOffset;
break;
case LC_DYLD_INFO:
case LC_DYLD_INFO_ONLY:
dyldInfo = (dyld_info_command*)cmd;
dyldInfo->rebase_off = 0;
dyldInfo->rebase_size = 0;
dyldInfo->bind_off = bindSize ? linkeditStartOffset + bindOffset : 0;
dyldInfo->bind_size = bindSize;
dyldInfo->weak_bind_off = weakBindSize ? linkeditStartOffset + weakBindOffset : 0;
dyldInfo->weak_bind_size = weakBindSize;
dyldInfo->lazy_bind_off = lazyBindSize ? linkeditStartOffset + lazyBindOffset : 0;
dyldInfo->lazy_bind_size = lazyBindSize;
dyldInfo->export_off = exportSize ? linkeditStartOffset + exportOffset : 0;
dyldInfo->export_size = exportSize;
break;
case LC_FUNCTION_STARTS:
functionStartsCmd = (linkedit_data_command*)cmd;
functionStartsCmd->dataoff = linkeditStartOffset + funcStartsOffset;
break;
case LC_DATA_IN_CODE:
dataInCodeCmd = (linkedit_data_command*)cmd;
dataInCodeCmd->dataoff = linkeditStartOffset + dataInCodeOffset;
break;
case LC_DYLD_CHAINED_FIXUPS:
chainedFixupsCmd = (linkedit_data_command*)cmd;
chainedFixupsCmd->dataoff = chainedFixupsSize ? linkeditStartOffset + chainedFixupsOffset : 0;
chainedFixupsCmd->datasize = chainedFixupsSize;
break;
case LC_DYLD_EXPORTS_TRIE:
exportTrieCmd = (linkedit_data_command*)cmd;
exportTrieCmd->dataoff = exportSize ? linkeditStartOffset + exportOffset : 0;
exportTrieCmd->datasize = exportSize;
break;
case macho_routines_command<P>::CMD:
routinesCmd = (macho_routines_command<P>*)cmd;
routinesCmd->set_init_address(routinesCmd->init_address()+slideForOrigAddress(routinesCmd->init_address()));
break;
case macho_segment_command<P>::CMD:
segCmd = (macho_segment_command<P>*)cmd;
segFileOffsetDelta = (int32_t)(_mappingInfo[segIndex].dstCacheFileOffset - segCmd->fileoff());
segCmd->set_vmaddr(_mappingInfo[segIndex].dstCacheUnslidAddress);
segCmd->set_vmsize(_mappingInfo[segIndex].dstCacheSegmentSize);
segCmd->set_fileoff(_mappingInfo[segIndex].dstCacheFileOffset);
segCmd->set_filesize(_mappingInfo[segIndex].dstCacheFileSize);
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
segCmd->set_vmsize(linkeditBufferSize);
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) {
bool coalescedSection = false;
if ( textCoalescer.sectionWasCoalesced(sect->segname(), sect->sectname())) {
coalescedSection = true;
}
#if BUILDING_APP_CACHE_UTIL
if ( strcmp(segCmd->segname(), "__CTF") == 0 ) {
if ( _mh->isStaticExecutable() )
coalescedSection = true;
}
#endif
if ( coalescedSection ) {
sect->set_addr(segCmd->vmaddr() + segCmd->filesize());
sect->set_offset(0);
sect->set_size(0);
} else {
sect->set_addr(sect->addr() + _segSlides[segIndex]);
if ( sect->offset() != 0 )
sect->set_offset(sect->offset() + segFileOffsetDelta);
}
}
}
++segIndex;
break;
case LC_SEGMENT_SPLIT_INFO:
splitSegInfoCmd = (linkedit_data_command*)cmd;
splitSegInfoCmd->dataoff = linkeditStartOffset + splitSegInfoOffset;
break;
default:
break;
}
});
_mh->removeLoadCommand(_diagnostics, ^(const load_command *cmd, bool &remove, bool &stop) {
switch ( cmd->cmd ) {
case LC_RPATH:
_diagnostics.warning("dyld shared cache does not support LC_RPATH found in %s", _dylibID);
remove = true;
break;
case LC_CODE_SIGNATURE:
case LC_DYLIB_CODE_SIGN_DRS:
remove = true;
break;
default:
break;
}
});
_mh->flags |= 0x80000000;
}
template <typename P>
void Adjustor<P>::adjustSymbolTable()
{
if ( _dynSymTabCmd == nullptr )
return;
macho_nlist<P>* symbolTable = (macho_nlist<P>*)&_linkeditBias[_symTabCmd->symoff];
macho_nlist<P>* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym + _dynSymTabCmd->nextdefsym];
for (macho_nlist<P>* entry = &symbolTable[_dynSymTabCmd->iextdefsym]; entry < lastExport; ++entry) {
if ( (entry->n_type() & N_TYPE) == N_SECT )
entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value()));
}
macho_nlist<P>* lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym+_dynSymTabCmd->nlocalsym];
for (macho_nlist<P>* entry = &symbolTable[_dynSymTabCmd->ilocalsym]; entry < lastLocal; ++entry) {
if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) )
entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value()));
}
}
template <typename P>
void Adjustor<P>::adjustChainedFixups(const CacheBuilder::DylibTextCoalescer& textCoalescer)
{
if ( _chainedFixupsCmd == nullptr )
return;
const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff];
uint64_t startsOffset = ((uint64_t)header + header->starts_offset) - (uint64_t)_mh;
_mh->withChainStarts(_diagnostics, startsOffset, ^(const dyld_chained_starts_in_image* starts) {
for (uint32_t segIndex=0; segIndex < starts->seg_count; ++segIndex) {
if ( starts->seg_info_offset[segIndex] == 0 )
continue;
dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]);
segInfo->segment_offset = (uint64_t)_mappingInfo[segIndex].dstSegment - (uint64_t)_mh;
if ( textCoalescer.segmentWasCoalesced(_segCmds[segIndex]->segname()) ) {
segInfo->page_count = 0;
}
}
});
}
template <typename P>
static uint64_t externalRelocBaseAddress(const dyld3::MachOAnalyzer* ma,
std::vector<macho_segment_command<P>*> segCmds,
std::vector<uint64_t> segOrigStartAddresses)
{
if ( ma->isArch("x86_64") || ma->isArch("x86_64h") ) {
#if BUILDING_APP_CACHE_UTIL
if ( ma->isKextBundle() ) {
return segOrigStartAddresses[0];
}
#endif
for (uint32_t i=0; i < segCmds.size(); ++i) {
if ( segCmds[i]->initprot() & VM_PROT_WRITE )
return segOrigStartAddresses[i];
}
}
return 0;
}
template <typename P>
void Adjustor<P>::adjustExternalRelocations()
{
if ( _dynSymTabCmd == nullptr )
return;
uint64_t baseAddress = _mappingInfo[0].dstCacheUnslidAddress;
const uint64_t relocsStartAddress = externalRelocBaseAddress(_mh, _segCmds, _segOrigStartAddresses);
relocation_info* relocsStart = (relocation_info*)&_linkeditBias[_dynSymTabCmd->extreloff];
relocation_info* relocsEnd = &relocsStart[_dynSymTabCmd->nextrel];
for (relocation_info* reloc = relocsStart; reloc < relocsEnd; ++reloc) {
uint64_t newAddress = reloc->r_address + slideForOrigAddress(relocsStartAddress + reloc->r_address);
newAddress -= baseAddress;
reloc->r_address = (int32_t)newAddress;
assert((uint64_t)reloc->r_address == newAddress);
}
}
template <typename P>
void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker)
{
pint_t* mappedAddrP = (pint_t*)((uint8_t*)_mappingInfo[segIndex].dstSegment + segOffset);
uint32_t* mappedAddr32 = (uint32_t*)mappedAddrP;
pint_t valueP;
uint32_t value32;
switch ( type ) {
case REBASE_TYPE_POINTER:
valueP = (pint_t)P::getP(*mappedAddrP);
P::setP(*mappedAddrP, valueP + slideForOrigAddress(valueP));
aslrTracker.add(mappedAddrP);
break;
case REBASE_TYPE_TEXT_ABSOLUTE32:
value32 = P::E::get32(*mappedAddr32);
P::E::set32(*mappedAddr32, value32 + (uint32_t)slideForOrigAddress(value32));
break;
case REBASE_TYPE_TEXT_PCREL32:
default:
_diagnostics.error("unknown rebase type 0x%02X in %s", type, _dylibID);
}
}
static bool isThumbMovw(uint32_t instruction)
{
return ( (instruction & 0x8000FBF0) == 0x0000F240 );
}
static bool isThumbMovt(uint32_t instruction)
{
return ( (instruction & 0x8000FBF0) == 0x0000F2C0 );
}
static uint16_t getThumbWord(uint32_t instruction)
{
uint32_t i = ((instruction & 0x00000400) >> 10);
uint32_t imm4 = (instruction & 0x0000000F);
uint32_t imm3 = ((instruction & 0x70000000) >> 28);
uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
return ((imm4 << 12) | (i << 11) | (imm3 << 8) | imm8);
}
static uint32_t setThumbWord(uint32_t instruction, uint16_t word) {
uint32_t imm4 = (word & 0xF000) >> 12;
uint32_t i = (word & 0x0800) >> 11;
uint32_t imm3 = (word & 0x0700) >> 8;
uint32_t imm8 = word & 0x00FF;
return (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16);
}
static bool isArmMovw(uint32_t instruction)
{
return (instruction & 0x0FF00000) == 0x03000000;
}
static bool isArmMovt(uint32_t instruction)
{
return (instruction & 0x0FF00000) == 0x03400000;
}
static uint16_t getArmWord(uint32_t instruction)
{
uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
uint32_t imm12 = (instruction & 0x00000FFF);
return (imm4 << 12) | imm12;
}
static uint32_t setArmWord(uint32_t instruction, uint16_t word) {
uint32_t imm4 = (word & 0xF000) >> 12;
uint32_t imm12 = word & 0x0FFF;
return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12;
}
template <typename P>
void Adjustor<P>::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker,
uint64_t targetSlide, bool convertRebaseChains)
{
assert(chainPtr->arm64e.authRebase.bind == 0);
assert( (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E)
|| (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND)
|| (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24)
|| (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_KERNEL) );
dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr;
dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
if ( chainPtr->arm64e.authRebase.auth ) {
uint64_t targetVMAddr = orgPtr.arm64e.authRebase.target + _segOrigStartAddresses[0] + targetSlide;
uint8_t high8 = targetVMAddr >> 56;
if ( high8 ) {
bool badPointer = true;
if ( _chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_KERNEL ) {
uint64_t vmOffset = targetVMAddr - _cacheBaseAddress;
if ( (vmOffset >> 56) == 0 )
badPointer = false;
}
if ( badPointer ) {
_diagnostics.error("Cannot set tag on pointer in '%s' as high bits are incompatible with pointer authentication", _dylibID);
return;
}
}
if ( _chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND ) {
aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
chainPtr->arm64e.rebase.target = 0; chainPtr->arm64e.rebase.high8 = 0;
chainPtr->arm64e.rebase.next = orgPtr.arm64e.rebase.next;
chainPtr->arm64e.rebase.bind = 0;
chainPtr->arm64e.rebase.auth = 0;
return;
}
if ( convertRebaseChains ) {
aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
chainPtr->raw64 = targetVMAddr;
return;
}
tmp.arm64e.authRebase.target = targetVMAddr;
if ( tmp.arm64e.authRebase.target == targetVMAddr ) {
chainPtr->arm64e.authRebase.target = targetVMAddr;
return;
}
tmp.arm64e.rebase.target = targetVMAddr;
if ( tmp.arm64e.rebase.target == targetVMAddr ) {
aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
chainPtr->arm64e.rebase.target = targetVMAddr;
chainPtr->arm64e.rebase.high8 = 0;
chainPtr->arm64e.rebase.next = orgPtr.arm64e.rebase.next;
chainPtr->arm64e.rebase.bind = 0;
chainPtr->arm64e.rebase.auth = 0;
return;
}
aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
chainPtr->arm64e.rebase.target = 0; chainPtr->arm64e.rebase.high8 = 0;
chainPtr->arm64e.rebase.next = orgPtr.arm64e.rebase.next;
chainPtr->arm64e.rebase.bind = 0;
chainPtr->arm64e.rebase.auth = 0;
return;
}
else {
uint64_t targetVMAddr = 0;
switch (_chainedFixupsFormat) {
case DYLD_CHAINED_PTR_ARM64E:
targetVMAddr = orgPtr.arm64e.rebase.target + targetSlide;
break;
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
aslrTracker.setRebaseTarget64(chainPtr, orgPtr.arm64e.rebase.target + targetSlide);
orgPtr.arm64e.rebase.target = 0;
targetVMAddr = 0;
break;
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
targetVMAddr = orgPtr.arm64e.rebase.target + _segOrigStartAddresses[0] + targetSlide;
break;
default:
_diagnostics.error("Unknown chain format");
return;
}
uint8_t high8 = targetVMAddr >> 56;
if ( chainPtr->arm64e.rebase.high8 ) {
if ( high8 ) {
_diagnostics.error("Cannot set tag on pointer as high bits are in use");
return;
}
aslrTracker.setHigh8(chainPtr, chainPtr->arm64e.rebase.high8);
} else {
if ( high8 ) {
aslrTracker.setHigh8(chainPtr, high8);
targetVMAddr &= 0x00FFFFFFFFFFFFFF;
}
}
if ( convertRebaseChains ) {
chainPtr->raw64 = targetVMAddr;
return;
}
tmp.arm64e.rebase.target = targetVMAddr;
if ( tmp.arm64e.rebase.target == targetVMAddr ) {
chainPtr->arm64e.rebase.target = targetVMAddr;
return;
}
aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
chainPtr->arm64e.rebase.target = 0; }
}
template <typename P>
void Adjustor<P>::convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide)
{
assert( (_chainedFixupsFormat == DYLD_CHAINED_PTR_64) || (_chainedFixupsFormat == DYLD_CHAINED_PTR_64_OFFSET) );
dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr;
dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
uint64_t targetVMAddr = 0;
switch (_chainedFixupsFormat) {
case DYLD_CHAINED_PTR_64:
targetVMAddr = orgPtr.generic64.rebase.target + targetSlide;
break;
case DYLD_CHAINED_PTR_64_OFFSET:
targetVMAddr = orgPtr.generic64.rebase.target + _segOrigStartAddresses[0] + targetSlide;
aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
chainPtr->generic64.rebase.target = 0;
return;
break;
default:
_diagnostics.error("Unknown chain format");
return;
}
tmp.generic64.rebase.target = targetVMAddr;
if ( tmp.generic64.rebase.target == targetVMAddr ) {
chainPtr->generic64.rebase.target = targetVMAddr;
return;
}
aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
chainPtr->generic64.rebase.target = 0; }
template <typename P>
void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress,
int64_t adjust, int64_t targetSlide, uint64_t imageStartAddress, uint64_t imageEndAddress,
bool convertRebaseChains,
CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress)
{
uint64_t value64;
uint64_t* mappedAddr64 = 0;
uint32_t value32;
uint32_t* mappedAddr32 = 0;
uint32_t instruction;
dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr;
uint32_t newPageOffset;
int64_t delta;
switch ( kind ) {
case DYLD_CACHE_ADJ_V2_DELTA_32:
mappedAddr32 = (uint32_t*)mappedAddr;
value32 = P::E::get32(*mappedAddr32);
delta = (int32_t)value32;
delta += adjust;
if ( (delta > 0x80000000) || (-delta > 0x80000000) ) {
_diagnostics.error("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _dylibID);
return;
}
P::E::set32(*mappedAddr32, (int32_t)delta);
break;
case DYLD_CACHE_ADJ_V2_POINTER_32:
mappedAddr32 = (uint32_t*)mappedAddr;
if ( _chainedFixupsCmd != nullptr ) {
chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr32;
switch (_chainedFixupsFormat) {
case DYLD_CHAINED_PTR_32:
if ( chainPtr->generic32.rebase.bind == 0 ) {
aslrTracker.add(mappedAddr32);
uint32_t target = (uint32_t)(chainPtr->generic32.rebase.target + targetSlide);
aslrTracker.setRebaseTarget32(chainPtr, target);
chainPtr->generic32.rebase.target = 0; }
break;
default:
_diagnostics.error("unknown 32-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID);
break;
}
}
else if ( _mh->usesClassicRelocationsInKernelCollection() ) {
if ( (uint32_t)toNewAddress != (uint32_t)(E::get32(*mappedAddr32) + targetSlide) ) {
_diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID);
return;
}
aslrTracker.setRebaseTarget32(mappedAddr32, (uint32_t)toNewAddress);
E::set32(*mappedAddr32, 0);
aslrTracker.add(mappedAddr32);
}
else {
if ( toNewAddress != (uint64_t)(E::get32(*mappedAddr32) + targetSlide) ) {
_diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID);
return;
}
E::set32(*mappedAddr32, (uint32_t)toNewAddress);
aslrTracker.add(mappedAddr32);
}
break;
case DYLD_CACHE_ADJ_V2_POINTER_64:
mappedAddr64 = (uint64_t*)mappedAddr;
if ( _chainedFixupsCmd != nullptr ) {
chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr64;
switch (_chainedFixupsFormat) {
case DYLD_CHAINED_PTR_ARM64E:
case DYLD_CHAINED_PTR_ARM64E_USERLAND:
case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
case DYLD_CHAINED_PTR_ARM64E_KERNEL:
if ( chainPtr->arm64e.authRebase.bind == 0 ) {
convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide, convertRebaseChains);
aslrTracker.add(chainPtr);
}
break;
case DYLD_CHAINED_PTR_64:
case DYLD_CHAINED_PTR_64_OFFSET:
if ( chainPtr->generic64.rebase.bind == 0 ) {
convertGeneric64RebaseToIntermediate(chainPtr, aslrTracker, targetSlide);
aslrTracker.add(chainPtr);
}
break;
default:
_diagnostics.error("unknown 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID);
break;
}
}
else if ( _mh->usesClassicRelocationsInKernelCollection() ) {
if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) {
_diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID);
return;
}
aslrTracker.setRebaseTarget64(mappedAddr64, toNewAddress);
E::set64(*mappedAddr64, 0); aslrTracker.add(mappedAddr64);
uint8_t high8 = toNewAddress >> 56;
if ( high8 )
aslrTracker.setHigh8(mappedAddr64, high8);
}
else {
if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) {
_diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID);
return;
}
E::set64(*mappedAddr64, toNewAddress);
aslrTracker.add(mappedAddr64);
uint8_t high8 = toNewAddress >> 56;
if ( high8 )
aslrTracker.setHigh8(mappedAddr64, high8);
}
break;
case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64:
chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr;
if ( chainPtr->arm64e.authRebase.bind == 0 ) {
convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide, convertRebaseChains);
aslrTracker.add(chainPtr);
}
break;
case DYLD_CACHE_ADJ_V2_DELTA_64:
mappedAddr64 = (uint64_t*)mappedAddr;
value64 = P::E::get64(*mappedAddr64);
E::set64(*mappedAddr64, value64 + adjust);
break;
case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
if ( adjust == 0 )
break;
mappedAddr32 = (uint32_t*)mappedAddr;
value32 = P::E::get32(*mappedAddr32);
value64 = toNewAddress - imageStartAddress;
if ( value64 > imageEndAddress ) {
_diagnostics.error("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _dylibID);
return;
}
P::E::set32(*mappedAddr32, (uint32_t)value64);
break;
case DYLD_CACHE_ADJ_V2_ARM64_ADRP:
mappedAddr32 = (uint32_t*)mappedAddr;
if (lohTracker)
(*lohTracker)[toNewAddress].insert(mappedAddr);
instruction = P::E::get32(*mappedAddr32);
if ( (instruction & 0x9F000000) == 0x90000000 ) {
int64_t pageDistance = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF));
int64_t newPage21 = pageDistance >> 12;
if ( (newPage21 > 2097151) || (newPage21 < -2097151) ) {
_diagnostics.error("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _dylibID);
return;
}
instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0);
P::E::set32(*mappedAddr32, instruction);
}
else {
}
break;
case DYLD_CACHE_ADJ_V2_ARM64_OFF12:
mappedAddr32 = (uint32_t*)mappedAddr;
if (lohTracker)
(*lohTracker)[toNewAddress].insert(mappedAddr);
instruction = P::E::get32(*mappedAddr32);
newPageOffset = (uint32_t)(toNewAddress & 0xFFF);
if ( (instruction & 0x3B000000) == 0x39000000 ) {
uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
uint32_t newAddend = 0;
switch ( instruction & 0xC0000000 ) {
case 0x00000000:
if ( (instruction & 0x04800000) == 0x04800000 ) {
if ( newPageOffset & 0xF ) {
_diagnostics.error("can't adjust off12 scale=16 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID);
return;
}
if ( encodedAddend*16 >= 4096 ) {
_diagnostics.error("off12 scale=16 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID);
}
newAddend = (newPageOffset/16);
}
else {
newAddend = newPageOffset;
}
break;
case 0x40000000:
if ( newPageOffset & 1 ) {
_diagnostics.error("can't adjust off12 scale=2 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID);
return;
}
if ( encodedAddend*2 >= 4096 ) {
_diagnostics.error("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID);
return;
}
newAddend = (newPageOffset/2);
break;
case 0x80000000:
if ( newPageOffset & 3 ) {
_diagnostics.error("can't adjust off12 scale=4 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID);
return;
}
if ( encodedAddend*4 >= 4096 ) {
_diagnostics.error("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID);
return;
}
newAddend = (newPageOffset/4);
break;
case 0xC0000000:
if ( newPageOffset & 7 ) {
_diagnostics.error("can't adjust off12 scale=8 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID);
return;
}
if ( encodedAddend*8 >= 4096 ) {
_diagnostics.error("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID);
return;
}
newAddend = (newPageOffset/8);
break;
}
uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
P::E::set32(*mappedAddr32, newInstruction);
}
else if ( (instruction & 0xFFC00000) == 0x91000000 ) {
if ( instruction & 0x00C00000 ) {
_diagnostics.error("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _dylibID);
return;
}
uint32_t newAddend = newPageOffset;
uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
P::E::set32(*mappedAddr32, newInstruction);
}
else if ( instruction != 0xD503201F ) {
_diagnostics.error("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _dylibID);
return;
}
break;
case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT:
mappedAddr32 = (uint32_t*)mappedAddr;
if ( lastKind == kind ) {
if ( lastToNewAddress == toNewAddress ) {
uint32_t instruction1 = P::E::get32(*lastMappedAddr32);
uint32_t instruction2 = P::E::get32(*mappedAddr32);
if ( isThumbMovw(instruction1) && isThumbMovt(instruction2) ) {
uint16_t high = getThumbWord(instruction2);
uint16_t low = getThumbWord(instruction1);
uint32_t full = high << 16 | low;
full += adjust;
instruction1 = setThumbWord(instruction1, full & 0xFFFF);
instruction2 = setThumbWord(instruction2, full >> 16);
}
else if ( isThumbMovt(instruction1) && isThumbMovw(instruction2) ) {
uint16_t high = getThumbWord(instruction1);
uint16_t low = getThumbWord(instruction2);
uint32_t full = high << 16 | low;
full += adjust;
instruction2 = setThumbWord(instruction2, full & 0xFFFF);
instruction1 = setThumbWord(instruction1, full >> 16);
}
else {
_diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _dylibID);
return;
}
P::E::set32(*lastMappedAddr32, instruction1);
P::E::set32(*mappedAddr32, instruction2);
kind = 0;
}
else {
_diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _dylibID);
return;
}
}
break;
case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT:
mappedAddr32 = (uint32_t*)mappedAddr;
if ( lastKind == kind ) {
if ( lastToNewAddress == toNewAddress ) {
uint32_t instruction1 = P::E::get32(*lastMappedAddr32);
uint32_t instruction2 = P::E::get32(*mappedAddr32);
if ( isArmMovw(instruction1) && isArmMovt(instruction2) ) {
uint16_t high = getArmWord(instruction2);
uint16_t low = getArmWord(instruction1);
uint32_t full = high << 16 | low;
full += adjust;
instruction1 = setArmWord(instruction1, full & 0xFFFF);
instruction2 = setArmWord(instruction2, full >> 16);
}
else if ( isArmMovt(instruction1) && isArmMovw(instruction2) ) {
uint16_t high = getArmWord(instruction1);
uint16_t low = getArmWord(instruction2);
uint32_t full = high << 16 | low;
full += adjust;
instruction2 = setArmWord(instruction2, full & 0xFFFF);
instruction1 = setArmWord(instruction1, full >> 16);
}
else {
_diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _dylibID);
return;
}
P::E::set32(*lastMappedAddr32, instruction1);
P::E::set32(*mappedAddr32, instruction2);
kind = 0;
}
else {
_diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _dylibID);
return;
}
}
break;
case DYLD_CACHE_ADJ_V2_ARM64_BR26: {
if ( adjust == 0 )
break;
mappedAddr32 = (uint32_t*)mappedAddr;
instruction = P::E::get32(*mappedAddr32);
int64_t deltaToFinalTarget = toNewAddress - fromNewAddress;
static const int64_t b128MegLimit = 0x07FFFFFF;
if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) {
instruction = (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF);
P::E::set32(*mappedAddr32, instruction);
break;
} else {
_diagnostics.error("br26 instruction exceeds maximum range at mapped address=%p in %s", mappedAddr, _dylibID);
return;
}
}
case DYLD_CACHE_ADJ_V2_THUMB_BR22:
case DYLD_CACHE_ADJ_V2_ARM_BR24:
break;
default:
_diagnostics.error("unknown split seg kind=%d in %s", kind, _dylibID);
return;
}
lastKind = kind;
lastToNewAddress = toNewAddress;
lastMappedAddr32 = mappedAddr32;
}
template <typename P>
void Adjustor<P>::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker,
CacheBuilder::LOH_Tracker* lohTracker,
const CacheBuilder::CacheCoalescedText* coalescedText,
const CacheBuilder::DylibTextCoalescer& textCoalescer)
{
static const bool logDefault = false;
bool log = logDefault;
const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff];
const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize];
if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) {
_diagnostics.error("malformed split seg info in %s", _dylibID);
return;
}
std::vector<uint64_t> sectionSlides;
std::vector<uint64_t> sectionNewAddress;
std::vector<uint8_t*> sectionMappedAddress;
typedef CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset DylibSectionOffsetToCacheSectionOffset;
std::vector<uint64_t> coalescedSectionOriginalVMAddrs;
std::vector<uint64_t> coalescedSectionNewVMAddrs;
std::vector<uint8_t*> coalescedSectionBufferAddrs;
std::vector<const DylibSectionOffsetToCacheSectionOffset*> coalescedSectionOffsetMaps;
std::vector<uint64_t> coalescedSectionObjcTags;
sectionSlides.reserve(16);
sectionNewAddress.reserve(16);
sectionMappedAddress.reserve(16);
coalescedSectionOriginalVMAddrs.reserve(16);
coalescedSectionNewVMAddrs.reserve(16);
coalescedSectionBufferAddrs.reserve(16);
coalescedSectionOffsetMaps.reserve(16);
coalescedSectionObjcTags.reserve(16);
sectionMappedAddress.push_back((uint8_t*)_mappingInfo[0].dstSegment);
sectionSlides.push_back(_segSlides[0]);
sectionNewAddress.push_back(_mappingInfo[0].dstCacheUnslidAddress);
coalescedSectionOriginalVMAddrs.push_back(0);
coalescedSectionNewVMAddrs.push_back(0);
coalescedSectionBufferAddrs.push_back(nullptr);
coalescedSectionOffsetMaps.push_back(nullptr);
coalescedSectionObjcTags.push_back(0);
uint64_t imageStartAddress = sectionNewAddress.front();
uint64_t imageEndAddress = 0;
unsigned sectionIndex = 0;
unsigned objcSelRefsSectionIndex = ~0U;
for (unsigned segmentIndex=0; segmentIndex < _segCmds.size(); ++segmentIndex) {
macho_segment_command<P>* segCmd = _segCmds[segmentIndex];
macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
if ( textCoalescer.sectionWasCoalesced(sect->segname(), sect->sectname())) {
const DylibSectionOffsetToCacheSectionOffset& offsetMap = textCoalescer.getSectionCoalescer(sect->segname(),
sect->sectname());
uint64_t coalescedSectionNewVMAddr = coalescedText->getSectionVMAddr(sect->segname(), sect->sectname());
uint8_t* coalescedSectionNewBufferAddr = coalescedText->getSectionBufferAddr(sect->segname(), sect->sectname());
uint64_t coalescedSectionObjcTag = coalescedText->getSectionObjcTag(sect->segname(), sect->sectname());
sectionMappedAddress.push_back(nullptr);
sectionSlides.push_back(0);
sectionNewAddress.push_back(0);
coalescedSectionOriginalVMAddrs.push_back(sect->addr());
coalescedSectionNewVMAddrs.push_back(coalescedSectionNewVMAddr);
coalescedSectionBufferAddrs.push_back(coalescedSectionNewBufferAddr);
coalescedSectionOffsetMaps.push_back(&offsetMap);
coalescedSectionObjcTags.push_back(coalescedSectionObjcTag);
++sectionIndex;
if (log) {
fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n",
sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back());
}
} else {
sectionMappedAddress.push_back((uint8_t*)_mappingInfo[segmentIndex].dstSegment + sect->addr() - segCmd->vmaddr());
sectionSlides.push_back(_segSlides[segmentIndex]);
sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheUnslidAddress + sect->addr() - segCmd->vmaddr());
coalescedSectionOriginalVMAddrs.push_back(0);
coalescedSectionNewVMAddrs.push_back(0);
coalescedSectionBufferAddrs.push_back(nullptr);
coalescedSectionOffsetMaps.push_back(nullptr);
coalescedSectionObjcTags.push_back(0);
++sectionIndex;
if (log) {
fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n",
sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back());
}
if (!strcmp(sect->segname(), "__DATA") && !strcmp(sect->sectname(), "__objc_selrefs"))
objcSelRefsSectionIndex = sectionIndex;
imageEndAddress = sectionNewAddress.back();
}
}
}
const uint8_t* p = infoStart;
uint64_t sectionCount = read_uleb128(p, infoEnd);
for (uint64_t i=0; i < sectionCount; ++i) {
uint32_t* lastMappedAddr32 = NULL;
uint32_t lastKind = 0;
uint64_t lastToNewAddress = 0;
uint64_t fromSectionIndex = read_uleb128(p, infoEnd);
uint64_t toSectionIndex = read_uleb128(p, infoEnd);
uint64_t toOffsetCount = read_uleb128(p, infoEnd);
uint64_t fromSectionSlide = sectionSlides[fromSectionIndex];
uint64_t fromSectionNewAddress = sectionNewAddress[fromSectionIndex];
uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex];
uint64_t toSectionSlide = sectionSlides[toSectionIndex];
uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex];
CacheBuilder::LOH_Tracker* lohTrackerPtr = (toSectionIndex == objcSelRefsSectionIndex) ? lohTracker : nullptr;
if (log) printf(" from sect=%lld (mapped=%p), to sect=%lld (new addr=0x%llX):\n", fromSectionIndex, fromSectionMappedAddress, toSectionIndex, toSectionNewAddress);
uint64_t toSectionOffset = 0;
for (uint64_t j=0; j < toOffsetCount; ++j) {
uint64_t toSectionDelta = read_uleb128(p, infoEnd);
uint64_t fromOffsetCount = read_uleb128(p, infoEnd);
toSectionOffset += toSectionDelta;
for (uint64_t k=0; k < fromOffsetCount; ++k) {
uint64_t kind = read_uleb128(p, infoEnd);
if ( kind > 13 ) {
_diagnostics.error("unknown split seg info v2 kind value (%llu) in %s", kind, _dylibID);
return;
}
uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
uint64_t fromSectionOffset = 0;
for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
uint64_t delta = read_uleb128(p, infoEnd);
fromSectionOffset += delta;
if (log) printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, delta, toSectionSlide);
uint8_t* fromMappedAddr = nullptr;
uint64_t fromNewAddress = 0;
uint64_t fromAtomSlide = 0;
bool convertRebaseChains = false;
if ( coalescedSectionOffsetMaps[fromSectionIndex] != nullptr ) {
assert( (kind == DYLD_CACHE_ADJ_V2_POINTER_64) || (kind == DYLD_CACHE_ADJ_V2_THREADED_POINTER_64) );
const DylibSectionOffsetToCacheSectionOffset* offsetMap = coalescedSectionOffsetMaps[fromSectionIndex];
auto offsetIt = offsetMap->lower_bound((uint32_t)fromSectionOffset);
if ( offsetIt->first != fromSectionOffset ) {
assert(offsetIt != offsetMap->begin());
--offsetIt;
assert(offsetIt->first <= fromSectionOffset);
}
assert(offsetIt != offsetMap->end());
uint64_t offsetInAtom = fromSectionOffset - offsetIt->first;
assert(offsetInAtom < (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize);
uint8_t* baseMappedAddr = coalescedSectionBufferAddrs[fromSectionIndex];
fromMappedAddr = baseMappedAddr + offsetIt->second + offsetInAtom;
uint64_t baseVMAddr = coalescedSectionNewVMAddrs[fromSectionIndex];
fromNewAddress = baseVMAddr + offsetIt->second + offsetInAtom;
uint64_t fromAtomOriginalVMAddr = coalescedSectionOriginalVMAddrs[fromSectionIndex] + fromSectionOffset;
fromAtomSlide = fromNewAddress - fromAtomOriginalVMAddr;
convertRebaseChains = true;
} else {
fromMappedAddr = fromSectionMappedAddress + fromSectionOffset;
fromNewAddress = fromSectionNewAddress + fromSectionOffset;
fromAtomSlide = fromSectionSlide;
}
uint64_t toNewAddress = 0;
uint64_t toAtomSlide = 0;
if ( coalescedSectionOffsetMaps[toSectionIndex] != nullptr ) {
const DylibSectionOffsetToCacheSectionOffset* offsetMap = coalescedSectionOffsetMaps[toSectionIndex];
auto offsetIt = offsetMap->find((uint32_t)toSectionOffset);
assert(offsetIt != offsetMap->end());
uint64_t baseVMAddr = coalescedSectionNewVMAddrs[toSectionIndex];
toNewAddress = baseVMAddr + offsetIt->second;
toNewAddress |= coalescedSectionObjcTags[toSectionIndex];
uint64_t toAtomOriginalVMAddr = coalescedSectionOriginalVMAddrs[toSectionIndex] + toSectionOffset;
toAtomSlide = toNewAddress - toAtomOriginalVMAddr;
} else {
toNewAddress = toSectionNewAddress + toSectionOffset;
toAtomSlide = toSectionSlide;
}
int64_t deltaAdjust = toAtomSlide - fromAtomSlide;
if (log) {
printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n",
kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide);
}
adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust,
toAtomSlide, imageStartAddress, imageEndAddress, convertRebaseChains,
aslrTracker, lohTrackerPtr,
lastMappedAddr32, lastKind, lastToNewAddress);
if ( _diagnostics.hasError() )
return;
}
}
}
}
}
template <typename P>
void Adjustor<P>::adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker)
{
const dyld_chained_fixups_header* chainHeader = (dyld_chained_fixups_header*)(&_linkeditBias[_chainedFixupsCmd->dataoff]);
const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)chainHeader + chainHeader->starts_offset);
_mh->forEachFixupInAllChains(_diagnostics, startsInfo, false,
^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
switch ( segInfo->pointer_format ) {
case DYLD_CHAINED_PTR_64:
if ( fixupLoc->generic64.rebase.bind == 0 ) {
uint64_t rebaseTargetInDylib = fixupLoc->generic64.rebase.target;
uint64_t rebaseTargetInDyldcache = fixupLoc->generic64.rebase.target + slideForOrigAddress(rebaseTargetInDylib);
convertGeneric64RebaseToIntermediate(fixupLoc, aslrTracker, rebaseTargetInDyldcache);
aslrTracker.add(fixupLoc);
}
break;
case DYLD_CHAINED_PTR_64_OFFSET:
_diagnostics.error("unhandled 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID);
break;
default:
_diagnostics.error("unsupported chained fixup format %d", segInfo->pointer_format);
stop = true;
}
});
}
static int uint32Sorter(const void* l, const void* r) {
if ( *((uint32_t*)l) < *((uint32_t*)r) )
return -1;
else
return 1;
}
template <typename P>
static uint64_t localRelocBaseAddress(const dyld3::MachOAnalyzer* ma,
std::vector<macho_segment_command<P>*> segCmds,
std::vector<uint64_t> segOrigStartAddresses)
{
if ( ma->isArch("x86_64") || ma->isArch("x86_64h") ) {
#if BUILDING_APP_CACHE_UTIL
if ( ma->isKextBundle() ) {
return segOrigStartAddresses[0];
}
#endif
for (uint32_t i=0; i < segCmds.size(); ++i) {
if ( segCmds[i]->initprot() & VM_PROT_WRITE )
return segOrigStartAddresses[i];
}
}
return segOrigStartAddresses[0];
}
static bool segIndexAndOffsetForAddress(uint64_t addr, const std::vector<uint64_t>& segOrigStartAddresses,
std::vector<uint64_t> segSizes, uint32_t& segIndex, uint64_t& segOffset)
{
for (uint32_t i=0; i < segOrigStartAddresses.size(); ++i) {
if ( (segOrigStartAddresses[i] <= addr) && (addr < (segOrigStartAddresses[i] + segSizes[i])) ) {
segIndex = i;
segOffset = addr - segOrigStartAddresses[i];
return true;
}
}
return false;
}
template <typename P>
void Adjustor<P>::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker)
{
if ( (_dynSymTabCmd != nullptr) && (_dynSymTabCmd->locreloff != 0) ) {
assert(_dyldInfo == nullptr);
const uint64_t relocsStartAddress = localRelocBaseAddress(_mh, _segCmds, _segOrigStartAddresses);
const relocation_info* const relocsStart = (const relocation_info* const)&_linkeditBias[_dynSymTabCmd->locreloff];
const relocation_info* const relocsEnd = &relocsStart[_dynSymTabCmd->nlocrel];
bool stop = false;
const uint8_t relocSize = (_mh->is64() ? 3 : 2);
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(uint32_t, relocAddrs, 2048);
for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
if ( reloc->r_length != relocSize ) {
_diagnostics.error("local relocation has wrong r_length");
break;
}
if ( reloc->r_type != 0 ) { _diagnostics.error("local relocation has wrong r_type");
break;
}
relocAddrs.push_back(reloc->r_address);
}
if ( !relocAddrs.empty() ) {
::qsort(&relocAddrs[0], relocAddrs.count(), sizeof(uint32_t), &uint32Sorter);
for (uint32_t addrOff : relocAddrs) {
uint32_t segIndex = 0;
uint64_t segOffset = 0;
if ( segIndexAndOffsetForAddress(relocsStartAddress+addrOff, _segOrigStartAddresses, _segSizes, segIndex, segOffset) ) {
uint8_t type = REBASE_TYPE_POINTER;
assert(_mh->cputype != CPU_TYPE_I386);
slidePointer(segIndex, segOffset, type, aslrTracker);
}
else {
_diagnostics.error("local relocation has out of range r_address");
break;
}
}
}
return;
}
if ( _dyldInfo == NULL )
return;
const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off];
const uint8_t* end = &p[_dyldInfo->rebase_size];
uint8_t type = 0;
int segIndex = 0;
uint64_t segOffset = 0;
uint64_t count;
uint64_t skip;
bool done = false;
while ( !done && (p < end) ) {
uint8_t immediate = *p & REBASE_IMMEDIATE_MASK;
uint8_t opcode = *p & REBASE_OPCODE_MASK;
++p;
switch (opcode) {
case REBASE_OPCODE_DONE:
done = true;
break;
case REBASE_OPCODE_SET_TYPE_IMM:
type = immediate;
break;
case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
segIndex = immediate;
segOffset = read_uleb128(p, end);
break;
case REBASE_OPCODE_ADD_ADDR_ULEB:
segOffset += read_uleb128(p, end);
break;
case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
segOffset += immediate*sizeof(pint_t);
break;
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
for (int i=0; i < immediate; ++i) {
slidePointer(segIndex, segOffset, type, aslrTracker);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
count = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
slidePointer(segIndex, segOffset, type, aslrTracker);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
slidePointer(segIndex, segOffset, type, aslrTracker);
segOffset += read_uleb128(p, end) + sizeof(pint_t);
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
slidePointer(segIndex, segOffset, type, aslrTracker);
segOffset += skip + sizeof(pint_t);
}
break;
default:
_diagnostics.error("unknown rebase opcode 0x%02X in %s", opcode, _dylibID);
done = true;
break;
}
}
}
template <typename P>
void Adjustor<P>::adjustInstruction(uint8_t kind, uint8_t* textLoc, uint64_t codeToDataDelta)
{
uint32_t* fixupLoc32 = (uint32_t*)textLoc;
uint64_t* fixupLoc64 = (uint64_t*)textLoc;
uint32_t instruction;
uint32_t value32;
uint64_t value64;
switch (kind) {
case 1: value32 = P::E::get32(*fixupLoc32);
value32 += codeToDataDelta;
P::E::set32(*fixupLoc32, value32);
break;
case 2: value64 = P::E::get64(*fixupLoc64);
value64 += codeToDataDelta;
P::E::set64(*fixupLoc64, value64);
break;
case 4: break;
case 5: instruction = P::E::get32(*fixupLoc32);
value32 = (instruction & 0x0000000F) + ((uint32_t)codeToDataDelta >> 12);
instruction = (instruction & 0xFFFFFFF0) | (value32 & 0x0000000F);
P::E::set32(*fixupLoc32, instruction);
break;
case 6: instruction = P::E::get32(*fixupLoc32);
value32 = ((instruction & 0x000F0000) >> 16) + ((uint32_t)codeToDataDelta >> 12);
instruction = (instruction & 0xFFF0FFFF) | ((value32 <<16) & 0x000F0000);
P::E::set32(*fixupLoc32, instruction);
break;
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1A:
case 0x1B:
case 0x1C:
case 0x1D:
case 0x1E:
case 0x1F:
{
instruction = P::E::get32(*fixupLoc32);
assert((instruction & 0x8000FBF0) == 0x0000F2C0);
uint32_t i = ((instruction & 0x00000400) >> 10);
uint32_t imm4 = (instruction & 0x0000000F);
uint32_t imm3 = ((instruction & 0x70000000) >> 28);
uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
uint32_t imm16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12);
uint32_t newTargetValue = targetValue + (uint32_t)codeToDataDelta;
uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28;
uint32_t i_ = (newTargetValue & 0x08000000) >> 27;
uint32_t imm3_ = (newTargetValue & 0x07000000) >> 24;
uint32_t imm8_ = (newTargetValue & 0x00FF0000) >> 16;
uint32_t newInstruction = (instruction & 0x8F00FBF0) | imm4_ | (i_ << 10) | (imm3_ << 28) | (imm8_ << 16);
P::E::set32(*fixupLoc32, newInstruction);
}
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2A:
case 0x2B:
case 0x2C:
case 0x2D:
case 0x2E:
case 0x2F:
{
instruction = P::E::get32(*fixupLoc32);
uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
uint32_t imm12 = (instruction & 0x00000FFF);
uint32_t imm16 = (imm4 << 12) | imm12;
uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12);
uint32_t newTargetValue = targetValue + (uint32_t)codeToDataDelta;
uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28;
uint32_t imm12_ = (newTargetValue & 0x0FFF0000) >> 16;
uint32_t newInstruction = (instruction & 0xFFF0F000) | (imm4_ << 16) | imm12_;
P::E::set32(*fixupLoc32, newInstruction);
}
break;
case 3: instruction = P::E::get32(*fixupLoc32);
if ( (instruction & 0x9F000000) == 0x90000000 ) {
value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9);
value64 += codeToDataDelta;
instruction = (instruction & 0x9F00001F) | ((value64 << 17) & 0x60000000) | ((value64 >> 9) & 0x00FFFFE0);
P::E::set32(*fixupLoc32, instruction);
}
break;
default:
break;
}
}
template <typename P>
void Adjustor<P>::adjustCode()
{
if ( _splitSegInfoCmd == nullptr )
return;
const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff];
const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize];;
uint64_t codeToDataDelta = _segSlides[1] - _segSlides[0];
for (const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) {
uint8_t kind = *p++;
uint8_t* textLoc = (uint8_t*)_mappingInfo[0].dstSegment;
while (uint64_t delta = read_uleb128(p, infoEnd)) {
textLoc += delta;
adjustInstruction(kind, textLoc, codeToDataDelta);
}
}
}
template <typename P>
void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& newTrieBytes)
{
uint32_t exportOffset = 0;
uint32_t exportSize = 0;
if ( _dyldInfo != nullptr ) {
exportOffset = _dyldInfo->export_off;
exportSize = _dyldInfo->export_size;
} else if (_exportTrieCmd != nullptr) {
exportOffset = _exportTrieCmd->dataoff;
exportSize = _exportTrieCmd->datasize;
}
if ( exportSize == 0 )
return;
const uint8_t* start = &_linkeditBias[exportOffset];
const uint8_t* end = &start[exportSize];
std::vector<ExportInfoTrie::Entry> originalExports;
if ( !ExportInfoTrie::parseTrie(start, end, originalExports) ) {
_diagnostics.error("malformed exports trie in %s", _dylibID);
return;
}
std::vector<ExportInfoTrie::Entry> newExports;
newExports.reserve(originalExports.size());
uint64_t baseAddress = _segOrigStartAddresses[0];
uint64_t baseAddressSlide = slideForOrigAddress(baseAddress);
for (auto& entry: originalExports) {
if ( (strncmp(entry.name.c_str(), "$ld$", 4) == 0)
|| (strncmp(entry.name.c_str(), ".objc_class_name",16) == 0)
|| (strncmp(entry.name.c_str(), ".objc_category_name",19) == 0) ) {
continue;
}
if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE )
entry.info.address += (slideForOrigAddress(entry.info.address + baseAddress) - baseAddressSlide);
newExports.push_back(entry);
}
newTrieBytes.reserve(exportSize);
ExportInfoTrie(newExports).emit(newTrieBytes);
while ( (newTrieBytes.size() % sizeof(pint_t)) != 0 )
newTrieBytes.push_back(0);
}
}
void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag,
uint64_t cacheBaseAddress,
CacheBuilder::ASLR_Tracker& aslrTracker,
CacheBuilder::LOH_Tracker* lohTracker,
const CacheBuilder::CacheCoalescedText* coalescedText) const
{
dyld3::MachOAnalyzer* mh = (dyld3::MachOAnalyzer*)dylib.cacheLocation[0].dstSegment;
if ( _is64 ) {
Adjustor<Pointer64<LittleEndian>> adjustor64(cacheBaseAddress,
mh,
dylib.dylibID.c_str(),
dylib.cacheLocation, diag);
adjustor64.adjustImageForNewSegmentLocations(aslrTracker, lohTracker, coalescedText, dylib.textCoalescer);
}
else {
Adjustor<Pointer32<LittleEndian>> adjustor32(cacheBaseAddress,
mh,
dylib.dylibID.c_str(),
dylib.cacheLocation, diag);
adjustor32.adjustImageForNewSegmentLocations(aslrTracker, lohTracker, coalescedText, dylib.textCoalescer);
}
}