LaunchCacheReader.cpp [plain text]
#include <stdint.h>
#include <assert.h>
#include <uuid/uuid.h>
#include <unistd.h>
#include <limits.h>
#include "LaunchCacheFormat.h"
#include "LaunchCache.h"
#include "MachOParser.h"
#include "DyldCacheParser.h"
namespace dyld {
extern void log(const char* format, ...) __attribute__((format(printf, 1, 2)));
}
namespace dyld3 {
namespace launch_cache {
static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end)
{
uint64_t result = 0;
int bit = 0;
do {
if (p == end) {
assert("malformed uleb128");
break;
}
uint64_t slice = *p & 0x7f;
if (bit > 63) {
assert("uleb128 too big for uint64");
break;
}
else {
result |= (slice << bit);
bit += 7;
}
} while (*p++ & 0x80);
return (uintptr_t)result;
}
bool MemoryRange::contains(const MemoryRange& other) const
{
if ( this->address > other.address )
return false;
const uint8_t* thisEnd = (uint8_t*)address + size;
const uint8_t* otherEnd = (uint8_t*)other.address + other.size;
return (thisEnd >= otherEnd);
}
bool MemoryRange::intersects(const MemoryRange& other) const
{
const uint8_t* thisEnd = (uint8_t*)address + size;
const uint8_t* otherEnd = (uint8_t*)other.address + other.size;
if ( otherEnd < this->address )
return false;
return ( other.address < thisEnd );
}
bool SlowLoadSet::contains(const BinaryImageData* image)
{
for (const BinaryImageData** p=_start; p < _current; ++p) {
if ( *p == image )
return true;
}
return false;
}
bool SlowLoadSet::add(const BinaryImageData* image)
{
if ( _current < _end ) {
*_current++ = image;
return true;
}
return false;
}
void SlowLoadSet::forEach(void (^handler)(const BinaryImageData*))
{
for (const BinaryImageData** p=_start; p < _current; ++p) {
handler(*p);
}
}
void SlowLoadSet::forEach(void (^handler)(const BinaryImageData*, bool& stop))
{
bool stop = false;
for (const BinaryImageData** p=_start; p < _current; ++p) {
handler(*p, stop);
if ( stop )
break;
}
}
long SlowLoadSet::count() const
{
return (_current - _start);
}
#if DYLD_IN_PROCESS
uintptr_t TargetSymbolValue::resolveTarget(Diagnostics& diag, const ImageGroup& inGroup, LoadedImages& images) const
{
MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) {
*foundMH = nullptr;
images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
Image anImage(binImage);
if ( strcmp(depLoadPath, anImage.path()) == 0 ) {
*foundMH = mh;
stop = true;
}
});
return (*foundMH != nullptr);
};
uintptr_t offset;
switch ( _data.sharedCache.kind ) {
case TargetSymbolValue::kindSharedCache:
assert(_data.sharedCache.offsetIntoCache != 0);
return (uintptr_t)(images.dyldCacheLoadAddressForImage() + _data.sharedCache.offsetIntoCache);
case TargetSymbolValue::kindAbsolute:
offset = (uintptr_t)_data.absolute.value;
if ( offset & 0x2000000000000000ULL )
offset |= 0xC000000000000000ULL;
return offset;
case TargetSymbolValue::kindGroup: {
uint32_t groupNum = _data.group.isIndirectGroup ? inGroup.indirectGroupNum(_data.group.groupNum) : _data.group.groupNum;
uintptr_t targetImageLoadAddress = (uintptr_t)(images.loadAddressFromGroupAndIndex(groupNum, _data.group.indexInGroup));
if ( targetImageLoadAddress == 0 )
diag.error("image for groupNum=%d, indexInGroup=%d not found", groupNum, _data.group.indexInGroup);
offset = (uintptr_t)_data.group.offsetInImage;
if ( offset & 0x0000020000000000ULL )
offset |= 0xFFFFFC0000000000ULL;
return targetImageLoadAddress + offset;
}
case TargetSymbolValue::kindDynamicGroup: {
const char* imagePath = inGroup.stringFromPool(_data.dynamicGroup.imagePathOffset);
const char* symbolName = inGroup.stringFromPool(_data.dynamicGroup.symbolNameOffset);
__block uintptr_t result = 0;
__block bool found = false;
if ( strcmp(imagePath, "@flat") == 0 ) {
images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
Diagnostics findSymbolDiag;
dyld3::MachOParser parser(mh);
dyld3::MachOParser::FoundSymbol foundInfo;
if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, ^(uint32_t, const char* depLoadPath, void*, const mach_header** foundMH, void**) {
*foundMH = nullptr;
images.forEachImage(^(uint32_t innerIndex, const BinaryImageData* innerBinImage, const mach_header* innerMH, bool& innerStop) {
Image innerImage(innerBinImage);
if ( strcmp(depLoadPath, innerImage.path()) == 0 ) {
*foundMH = innerMH;
innerStop = true;
}
});
return (*foundMH != nullptr);
}) ) {
switch (foundInfo.kind) {
case MachOParser::FoundSymbol::Kind::headerOffset:
case MachOParser::FoundSymbol::Kind::resolverOffset:
result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
break;
case MachOParser::FoundSymbol::Kind::absolute:
result = (uintptr_t)foundInfo.value;
break;
}
images.setAsNeverUnload(idx);
found = true;
stop = true;
}
});
if ( !found ) {
result = 0;
found = true;
}
}
else if ( strcmp(imagePath, "@main") == 0 ) {
images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
if ( mh->filetype == MH_EXECUTE ) {
Diagnostics findSymbolDiag;
dyld3::MachOParser parser(mh);
dyld3::MachOParser::FoundSymbol foundInfo;
if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, nullptr) ) {
switch (foundInfo.kind) {
case MachOParser::FoundSymbol::Kind::headerOffset:
case MachOParser::FoundSymbol::Kind::resolverOffset:
result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
break;
case MachOParser::FoundSymbol::Kind::absolute:
result = (uintptr_t)foundInfo.value;
break;
}
found = true;
stop = true;
}
}
});
}
else if ( strcmp(imagePath, "@weak_def") == 0 ) {
images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
Image anImage(binImage);
if ( anImage.hasWeakDefs() ) {
Diagnostics findSymbolDiag;
dyld3::MachOParser parser(mh);
dyld3::MachOParser::FoundSymbol foundInfo;
if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, nullptr) ) {
switch (foundInfo.kind) {
case MachOParser::FoundSymbol::Kind::headerOffset:
case MachOParser::FoundSymbol::Kind::resolverOffset:
result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
break;
case MachOParser::FoundSymbol::Kind::absolute:
result = (uintptr_t)foundInfo.value;
break;
}
found = true;
images.setAsNeverUnload(idx);
stop = true;
}
}
});
}
else {
images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) {
Image anImage(binImage);
if ( strcmp(anImage.path(), imagePath) == 0 ) {
Diagnostics findSymbolDiag;
dyld3::MachOParser parser(mh);
dyld3::MachOParser::FoundSymbol foundInfo;
if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, reExportFollower) ) {
switch (foundInfo.kind) {
case MachOParser::FoundSymbol::Kind::headerOffset:
case MachOParser::FoundSymbol::Kind::resolverOffset:
result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
break;
case MachOParser::FoundSymbol::Kind::absolute:
result = (uintptr_t)foundInfo.value;
break;
}
found = true;
stop = true;
}
}
});
}
if ( found )
return result;
if ( _data.dynamicGroup.weakImport )
return 0;
diag.error("dynamic symbol '%s' not found for %s", symbolName, imagePath);
return 0;
}
}
assert(0 && "resolveTarget() not reachable");
}
#else
TargetSymbolValue::TargetSymbolValue()
{
_data.raw = 0;
}
TargetSymbolValue TargetSymbolValue::makeInvalid()
{
return TargetSymbolValue();
}
TargetSymbolValue TargetSymbolValue::makeSharedCacheOffset(uint32_t offset)
{
TargetSymbolValue t;
t._data.sharedCache.kind = kindSharedCache;
t._data.sharedCache.offsetIntoCache = offset;
return t;
}
TargetSymbolValue TargetSymbolValue::makeAbsolute(uint64_t value)
{
TargetSymbolValue t;
t._data.absolute.kind = kindAbsolute;
t._data.absolute.value = value;
return t;
}
TargetSymbolValue TargetSymbolValue::makeGroupValue(uint32_t groupIndex, uint32_t imageIndexInGroup, uint64_t offsetInImage, bool isIndirectGroupNum)
{
assert(groupIndex != 0 || isIndirectGroupNum);
assert(groupIndex < 128);
assert(imageIndexInGroup < 4096);
TargetSymbolValue t;
t._data.group.kind = kindGroup;
t._data.group.isIndirectGroup = isIndirectGroupNum;
t._data.group.groupNum = groupIndex;
t._data.group.indexInGroup = imageIndexInGroup;
t._data.group.offsetInImage = offsetInImage;
return t;
}
TargetSymbolValue TargetSymbolValue::makeDynamicGroupValue(uint32_t imagePathPoolOffset, uint32_t imageSymbolPoolOffset, bool weakImport)
{
TargetSymbolValue t;
t._data.dynamicGroup.kind = kindDynamicGroup;
t._data.dynamicGroup.weakImport = weakImport;
t._data.dynamicGroup.imagePathOffset = imagePathPoolOffset;
t._data.dynamicGroup.symbolNameOffset = imageSymbolPoolOffset;
return t;
}
bool TargetSymbolValue::isSharedCacheTarget(uint64_t& offsetInCache) const
{
if ( _data.sharedCache.kind != kindSharedCache )
return false;
offsetInCache = _data.sharedCache.offsetIntoCache;
return true;
}
bool TargetSymbolValue::isGroupImageTarget(uint32_t& groupNum, uint32_t& indexInGroup, uint64_t& offsetInImage) const
{
if ( _data.sharedCache.kind != kindGroup )
return false;
assert(!_data.group.isIndirectGroup);
groupNum = _data.group.groupNum;
indexInGroup = _data.group.indexInGroup;
offsetInImage = _data.group.offsetInImage;
return true;
}
bool TargetSymbolValue::isInvalid() const
{
return (_data.raw == 0);
}
static std::string hex8(uint64_t value) {
char buff[64];
sprintf(buff, "0x%08llX", value);
return buff;
}
static std::string decimal(uint64_t value) {
char buff[64];
sprintf(buff, "%llu", value);
return buff;
}
std::string TargetSymbolValue::asString(ImageGroup group) const
{
int64_t offset;
switch ( _data.sharedCache.kind ) {
case kindSharedCache:
if ( _data.sharedCache.offsetIntoCache == 0 )
return "{invalid target}";
else
return "{cache+" + hex8(_data.sharedCache.offsetIntoCache) + "}";
case kindAbsolute:
offset = (uintptr_t)_data.absolute.value;
if ( offset & 0x2000000000000000ULL )
offset |= 0xC000000000000000ULL;
return "{absolute:" + hex8(offset) + "}";
case kindGroup:
offset = _data.group.offsetInImage;
if ( offset & 0x0000020000000000ULL )
offset |= 0xFFFFFC0000000000ULL;
if ( _data.group.groupNum == 1 )
return "{otherDylib[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
if ( _data.group.groupNum == 2 )
return "{closure[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
else {
uint32_t groupNum = _data.group.isIndirectGroup ? group.indirectGroupNum(_data.group.groupNum) : _data.group.groupNum;
return "{dlopen-group-" + decimal(groupNum-2) + "[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}";
}
case kindDynamicGroup:
return "{dynamic image='" + std::string(group.stringFromPool(_data.dynamicGroup.imagePathOffset))
+ "' symbol='" + std::string(group.stringFromPool(_data.dynamicGroup.symbolNameOffset)) + "'}";
}
assert(0 && "unreachable");
return "xx";
}
#endif
binary_format::ImageRef binary_format::ImageRef::weakImportMissing()
{
ImageRef missing(0xFFFFFFFF);
return missing;
}
Closure::Closure(const binary_format::Closure* closure)
: _binaryData(closure)
{
assert(closure->magic == binary_format::Closure::magicV1);
}
size_t Closure::size() const
{
return _binaryData->stringPoolOffset + _binaryData->stringPoolSize;
}
const ImageGroup Closure::group() const
{
return ImageGroup(&_binaryData->group);
}
void Closure::forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const
{
const uint32_t* envVarStringOffsets = (uint32_t*)((uint8_t*)_binaryData + _binaryData->dyldEnvVarsOffset);
const char* stringPool = (char*)_binaryData + _binaryData->stringPoolOffset;
bool stop = false;
for (uint32_t i=0; i < _binaryData->dyldEnvVarsCount; ++i) {
handler(&stringPool[envVarStringOffsets[i]], stop);
if ( stop )
break;
}
}
void Closure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const
{
const uint16_t* offsets = (uint16_t*)((uint8_t*)_binaryData + _binaryData->missingFileComponentsOffset);
if ( *offsets == 0 )
return;
const char* stringPool = (char*)_binaryData + _binaryData->stringPoolOffset;
bool stop = false;
while ( !stop ) {
char path[PATH_MAX];
path[0] = '\0';
while ( *offsets != 0 ) {
const char* component = &stringPool[*offsets++];
strlcat(path, "/", PATH_MAX);
strlcat(path, component, PATH_MAX);
}
handler(path, stop);
++offsets; if ( *offsets == 0 ) stop = true;
}
}
const uuid_t* Closure::dyldCacheUUID() const
{
return &(_binaryData->dyldCacheUUID);
}
const uint8_t* Closure::cdHash() const
{
return _binaryData->mainExecutableCdHash;
}
uint32_t Closure::initialImageCount() const
{
return _binaryData->initialImageCount;
}
uint32_t Closure::mainExecutableImageIndex() const
{
return _binaryData->mainExecutableIndexInGroup;
}
uint32_t Closure::mainExecutableEntryOffset() const
{
return _binaryData->mainExecutableEntryOffset;
}
bool Closure::mainExecutableUsesCRT() const
{
return _binaryData->usesCRT;
}
bool Closure::isRestricted() const
{
return _binaryData->isRestricted;
}
bool Closure::usesLibraryValidation() const
{
return _binaryData->usesLibraryValidation;
}
uint32_t Closure::libdyldVectorOffset() const
{
return _binaryData->libdyldVectorOffset;
}
const BinaryImageData* Closure::libSystem(const ImageGroupList& groups)
{
return Image::resolveImageRef(groups, _binaryData->libSystemRef).binaryData();
}
const BinaryImageData* Closure::libDyld(const ImageGroupList& groups)
{
return Image::resolveImageRef(groups, _binaryData->libDyldRef).binaryData();
}
size_t ImageGroup::size() const
{
return (_binaryData->stringsPoolOffset + _binaryData->stringsPoolSize + 3) & (-4);
}
uint32_t ImageGroup::groupNum() const
{
return _binaryData->groupNum;
}
bool ImageGroup::dylibsExpectedOnDisk() const
{
return _binaryData->dylibsExpectedOnDisk;
}
uint32_t ImageGroup::imageCount() const
{
return _binaryData->imagesPoolCount;
}
const binary_format::Image* ImageGroup::imageBinary(uint32_t index) const
{
assert(index <_binaryData->imagesPoolCount);
return (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset + (index * _binaryData->imagesEntrySize));
}
const Image ImageGroup::image(uint32_t index) const
{
return Image(imageBinary(index));
}
uint32_t ImageGroup::indexInGroup(const binary_format::Image* img) const
{
long delta = (char*)img - ((char*)_binaryData + _binaryData->imagesPoolOffset);
uint32_t index = (uint32_t)(delta /_binaryData->imagesEntrySize);
assert(image(index)._binaryData == img);
return index;
}
const binary_format::Image* ImageGroup::findImageByPath(const char* path, uint32_t& foundIndex) const
{
uint32_t targetHash = hashFunction(path);
const uint8_t* p = (uint8_t*)_binaryData + _binaryData->imagesPoolOffset;
for (uint32_t i=0; i < _binaryData->imagesPoolCount; ++i) {
const binary_format::Image* binImage = (binary_format::Image*)p;
if ( binImage->pathHash == targetHash ) {
Image img(binImage);
if ( !img.isInvalid() && (strcmp(img.path(), path) == 0) ) {
foundIndex = i;
return binImage;
}
}
p += _binaryData->imagesEntrySize;
}
const binary_format::AliasEntry* aliasEntries = (binary_format::AliasEntry*)((uint8_t*)_binaryData + _binaryData->imageAliasOffset);
for (uint32_t i=0; i < _binaryData->imageAliasCount; ++i) {
const char* aliasPath = stringFromPool(aliasEntries[i].aliasOffsetInStringPool);
if ( aliasEntries[i].aliasHash == targetHash ) {
if ( strcmp(aliasPath, path) == 0 ) {
Image img = image(aliasEntries[i].imageIndexInGroup);
if ( !img.isInvalid() ) {
foundIndex = aliasEntries[i].imageIndexInGroup;
return img.binaryData();
}
}
}
}
return nullptr;
}
const binary_format::Image* ImageGroup::findImageByCacheOffset(size_t cacheVmOffset, uint32_t& mhCacheOffset, uint8_t& foundPermissions) const
{
assert(groupNum() == 0);
const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)segmentPool(0);
const binary_format::Image* image = (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset);
for (uint32_t imageIndex=0; imageIndex < _binaryData->imagesPoolCount; ++imageIndex) {
const binary_format::DyldCacheSegment* segInfo = &cacheSegs[image->segmentsArrayStartIndex];
if ( (cacheVmOffset >= segInfo->cacheOffset) && (cacheVmOffset < (segInfo->cacheOffset + segInfo->size)) ) {
mhCacheOffset = segInfo->cacheOffset;
foundPermissions = segInfo->permissions;
return image;
}
image = (binary_format::Image*)((char*)image + _binaryData->imagesEntrySize);
}
image = (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset);
for (uint32_t imageIndex=0; imageIndex < _binaryData->imagesPoolCount; ++imageIndex) {
for (uint32_t segIndex=1; segIndex < image->segmentsArrayCount; ++segIndex) {
const binary_format::DyldCacheSegment* segInfo = &cacheSegs[image->segmentsArrayStartIndex+segIndex];
if ( (cacheVmOffset >= segInfo->cacheOffset) && (cacheVmOffset < (segInfo->cacheOffset + segInfo->size)) ) {
mhCacheOffset = cacheSegs[image->segmentsArrayStartIndex].cacheOffset;
foundPermissions = segInfo->permissions;
return image;
}
}
image = (binary_format::Image*)((char*)image + _binaryData->imagesEntrySize);
}
return nullptr;
}
void ImageGroup::forEachAliasOf(uint32_t imageIndex, void (^handler)(const char* aliasPath, uint32_t aliasPathHash, bool& stop)) const
{
bool stop = false;
const binary_format::AliasEntry* aliasEntries = (binary_format::AliasEntry*)((uint8_t*)_binaryData + _binaryData->imageAliasOffset);
for (uint32_t i=0; i < _binaryData->imageAliasCount; ++i) {
if ( aliasEntries[i].imageIndexInGroup == imageIndex ) {
const char* aliasPath = stringFromPool(aliasEntries[i].aliasOffsetInStringPool);
handler(aliasPath, aliasEntries[i].aliasHash, stop);
if ( stop )
break;
}
}
}
const char* ImageGroup::stringPool() const
{
return (char*)_binaryData + _binaryData->stringsPoolOffset;
}
const char* ImageGroup::stringFromPool(uint32_t offset) const
{
assert(offset < _binaryData->stringsPoolSize);
return (char*)_binaryData + _binaryData->stringsPoolOffset + offset;
}
uint32_t ImageGroup::stringPoolSize() const
{
return _binaryData->stringsPoolSize;;
}
binary_format::ImageRef ImageGroup::dependentPool(uint32_t index) const
{
assert(index < _binaryData->dependentsPoolCount);
const binary_format::ImageRef* depArray = (binary_format::ImageRef*)((char*)_binaryData + _binaryData->dependentsPoolOffset);
return depArray[index];
}
const uint64_t* ImageGroup::segmentPool(uint32_t index) const
{
assert(index < _binaryData->segmentsPoolCount);
const uint64_t* segArray = (uint64_t*)((char*)_binaryData + _binaryData->segmentsPoolOffset);
return &segArray[index];
}
const uint32_t* ImageGroup::initializerOffsetsPool() const
{
return (uint32_t*)((char*)_binaryData + _binaryData->intializerOffsetPoolOffset);
}
const uint32_t ImageGroup::initializerOffsetsCount() const
{
return _binaryData->intializerOffsetPoolCount;
}
const binary_format::ImageRef* ImageGroup::intializerListPool() const
{
return (binary_format::ImageRef*)((char*)_binaryData + _binaryData->intializerListPoolOffset);
}
const uint32_t ImageGroup::intializerListPoolCount() const
{
return _binaryData->intializerListPoolCount;
}
const binary_format::AllFixupsBySegment* ImageGroup::fixUps(uint32_t offset) const
{
return (binary_format::AllFixupsBySegment*)((char*)_binaryData + _binaryData->fixupsOffset + offset);
}
const TargetSymbolValue* ImageGroup::targetValuesArray() const
{
return (TargetSymbolValue*)((char*)_binaryData + _binaryData->targetsOffset);
}
uint32_t ImageGroup::targetValuesCount() const
{
return _binaryData->targetsPoolCount;
}
const uint32_t* ImageGroup::dofOffsetsPool() const
{
return (uint32_t*)((char*)_binaryData + _binaryData->dofOffsetPoolOffset);
}
const uint32_t ImageGroup::dofOffsetsCount() const
{
return _binaryData->dofOffsetPoolCount;
}
const uint32_t* ImageGroup::indirectGroupNumsPool() const
{
return (uint32_t*)((char*)_binaryData + _binaryData->indirectGroupNumPoolOffset);
}
const uint32_t ImageGroup::indirectGroupNumsCount() const
{
return _binaryData->indirectGroupNumPoolCount;
}
uint32_t ImageGroup::indirectGroupNum(uint32_t offset) const
{
assert(offset < _binaryData->indirectGroupNumPoolCount);
return indirectGroupNumsPool()[offset];
}
uint32_t ImageGroup::hashFunction(const char* str)
{
uint32_t h = 0;
for (const char* s=str; *s != '\0'; ++s)
h = h*5 + *s;
return h;
}
void ImageGroup::forEachDyldCachePatch(uint32_t patchTargetIndex, uint32_t cacheDataVmOffset, void (^handler)(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop)) const
{
assert(_binaryData->imagesEntrySize == sizeof(binary_format::CachedImage) && "only callable on group-0 in shared cache");
assert(patchTargetIndex < _binaryData->cachePatchTableCount);
const binary_format::PatchTable* patches = (binary_format::PatchTable*)((char*)_binaryData + _binaryData->cachePatchTableOffset);
uint32_t offsetsIndex = patches[patchTargetIndex].offsetsStartIndex;
uint32_t targetCacheOffset = patches[patchTargetIndex].targetCacheOffset;
const binary_format::PatchOffset* patchLocationOffsets = (binary_format::PatchOffset*)((char*)_binaryData + _binaryData->cachePatchOffsetsOffset);
bool stop = false;
while ( !stop ) {
assert(offsetsIndex < _binaryData->cachePatchOffsetsCount);
binary_format::PatchOffset entry = patchLocationOffsets[offsetsIndex];
++offsetsIndex;
handler(targetCacheOffset, cacheDataVmOffset+entry.dataRegionOffset, entry.hasAddend, stop);
if ( entry.last )
stop = true;
}
}
void ImageGroup::forEachImageRefOverride(void (^handler)(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool& stop)) const
{
bool stop = false;
const binary_format::ImageRefOverride* entries = (binary_format::ImageRefOverride*)((char*)_binaryData + _binaryData->imageOverrideTableOffset);
for (uint32_t i=0; (i < _binaryData->imageOverrideTableCount) && !stop; ++i) {
handler(entries[i].standardDylib, entries[i].overrideDylib, stop);
}
}
void ImageGroup::forEachImageRefOverride(const ImageGroupList& groupList, void (^handler)(Image standardDylib, Image overrideDylib, bool& stop)) const
{
forEachImageRefOverride(^(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool& stop) {
Image standardDylib = Image::resolveImageRef(groupList, standardDylibRef, false);
Image overrideDylib = Image::resolveImageRef(groupList, overrideDylibRef, false);
handler(standardDylib, overrideDylib, stop);
});
}
#if DYLD_IN_PROCESS
void ImageGroup::forEachDyldCachePatchLocation(const void* dyldCacheLoadAddress, uint32_t patchTargetIndex, void (^handler)(uintptr_t* locationToPatch, uintptr_t addend, bool&)) const
{
DyldCacheParser cacheParser((DyldSharedCache*)dyldCacheLoadAddress, false);
uint32_t cacheDataVmOffset = (uint32_t)cacheParser.dataRegionRuntimeVmOffset();
forEachDyldCachePatch(patchTargetIndex, cacheDataVmOffset, ^(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop) {
uintptr_t addend = 0;
uintptr_t* fixupLoc = (uintptr_t*)((char*)dyldCacheLoadAddress + usePointersCacheOffset);
if ( hasAddend ) {
uintptr_t currentValue = *fixupLoc;
uintptr_t expectedValue = (uintptr_t)dyldCacheLoadAddress + targetCacheOffset;
uintptr_t delta = currentValue - expectedValue;
assert(delta < 32);
addend = delta;
}
handler(fixupLoc, addend, stop);
});
}
void ImageGroup::forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, const BinaryImageData* image, uint32_t imageOffset, bool& stop)) const
{
bool stop = false;
const binary_format::DyldCacheOverride* entries = (binary_format::DyldCacheOverride*)((char*)_binaryData + _binaryData->symbolOverrideTableOffset);
for (uint32_t i=0; (i < _binaryData->symbolOverrideTableCount) && !stop; ++i) {
handler(entries[i].patchTableIndex, imageBinary(entries[i].imageIndex), entries[i].imageOffset, stop);
}
}
#else
void ImageGroup::forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop)) const
{
bool stop = false;
const binary_format::DyldCacheOverride* entries = (binary_format::DyldCacheOverride*)((char*)_binaryData + _binaryData->symbolOverrideTableOffset);
for (uint32_t i=0; (i < _binaryData->symbolOverrideTableCount) && !stop; ++i) {
handler(entries[i].patchTableIndex, entries[i].imageIndex, entries[i].imageOffset, stop);
}
}
void ImageGroup::forEachDyldCachePatchLocation(const DyldCacheParser& cacheParser, void (^handler)(uint32_t targetCacheOffset, const std::vector<uint32_t>& usesPointersCacheOffsets, bool& stop)) const
{
uint32_t cacheDataVmOffset = (uint32_t)cacheParser.dataRegionRuntimeVmOffset();
__block std::vector<uint32_t> pointerCacheOffsets;
bool stop = false;
for (uint32_t patchIndex=0; patchIndex < _binaryData->cachePatchTableCount; ++patchIndex) {
pointerCacheOffsets.clear();
__block uint32_t targetCacheOffset = 0;
forEachDyldCachePatch(patchIndex, cacheDataVmOffset, ^(uint32_t targetCacheOff, uint32_t usePointersCacheOffset, bool hasAddend, bool&) {
targetCacheOffset = targetCacheOff;
pointerCacheOffsets.push_back(usePointersCacheOffset);
});
std::sort(pointerCacheOffsets.begin(), pointerCacheOffsets.end(), [&](uint32_t a, uint32_t b) { return a < b; });
handler(targetCacheOffset, pointerCacheOffsets, stop);
if ( stop )
break;
}
}
bool ImageGroup::hasPatchTableIndex(uint32_t targetCacheOffset, uint32_t& foundIndex) const
{
const binary_format::PatchTable* patches = (binary_format::PatchTable*)((char*)_binaryData + _binaryData->cachePatchTableOffset);
for (uint32_t i=0; i < _binaryData->cachePatchTableCount; ++i) {
if ( patches[i].targetCacheOffset == targetCacheOffset ) {
foundIndex = i;
return true;
}
}
return false;
}
#endif
const ImageGroup Image::group() const
{
return ImageGroup((binary_format::ImageGroup*)(((char*)_binaryData) + (_binaryData->groupOffset)));
}
uint32_t Image::maxLoadCount() const
{
return _binaryData->maxLoadCount;
}
const char* Image::path() const
{
return group().stringFromPool(_binaryData->pathPoolOffset);
}
uint32_t Image::pathHash() const
{
return _binaryData->pathHash;
}
const char* Image::leafName() const
{
const char* path = group().stringFromPool(_binaryData->pathPoolOffset);
const char* lastSlash = strrchr(path, '/');
if ( lastSlash != nullptr )
return lastSlash+1;
else
return path;
}
const uuid_t* Image::uuid() const
{
return &(_binaryData->uuid);
}
bool Image::isInvalid() const
{
return (_binaryData == nullptr) || _binaryData->isInvalid;
}
bool Image::hasObjC() const
{
return _binaryData->hasObjC;
}
bool Image::isBundle() const
{
return _binaryData->isBundle;
}
bool Image::hasWeakDefs() const
{
return _binaryData->hasWeakDefs;
}
bool Image::mayHavePlusLoads() const
{
return _binaryData->mayHavePlusLoads;
}
bool Image::hasTextRelocs() const
{
return _binaryData->hasTextRelocs;
}
bool Image::neverUnload() const
{
return _binaryData->neverUnload;
}
bool Image::cwdMustBeThisDir() const
{
return _binaryData->cwdSameAsThis;
}
bool Image::isPlatformBinary() const
{
return _binaryData->isPlatformBinary;
}
bool Image::overridableDylib() const
{
return _binaryData->overridableDylib;
}
void Image::forEachDependentImage(const ImageGroupList& groups, void (^handler)(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop)) const
{
assert(!_binaryData->isInvalid);
binary_format::ImageRef missingRef = binary_format::ImageRef::weakImportMissing();
__block bool stop = false;
for (uint32_t depIndex=0; (depIndex < _binaryData->dependentsArrayCount) && !stop; ++depIndex) {
binary_format::ImageRef ref = group().dependentPool(_binaryData->dependentsArrayStartIndex + depIndex);
if ( ref != missingRef ) {
Image depImage(resolveImageRef(groups, ref));
handler(depIndex, depImage, (LinkKind)ref.kind(), stop);
}
}
}
#if !DYLD_IN_PROCESS
bool Image::recurseAllDependentImages(const ImageGroupList& groups, std::unordered_set<const BinaryImageData*>& allDependents) const
{
if ( isInvalid() )
return false;
__block bool result = true;
forEachDependentImage(groups, ^(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop) {
if ( allDependents.count(depImage.binaryData()) == 0 ) {
allDependents.insert(depImage.binaryData());
if ( !depImage.recurseAllDependentImages(groups, allDependents) ) {
result = false;
stop = true;
}
}
});
return result;
}
#endif
bool Image::recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, bool& stopped,
void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const
{
__block bool result = true;
const dyld3::launch_cache::binary_format::Image* needToProcessArray[_binaryData->dependentsArrayCount];
memset((void*)needToProcessArray, 0, _binaryData->dependentsArrayCount * sizeof(*needToProcessArray));
const dyld3::launch_cache::binary_format::Image** const needToProcess = needToProcessArray;
forEachDependentImage(groups, ^(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop) {
const dyld3::launch_cache::binary_format::Image* depImageData = depImage.binaryData();
if ( allDependents.contains(depImageData) ) {
needToProcess[depIndex] = nullptr;
}
else {
needToProcess[depIndex] = depImageData;
if ( !allDependents.add(depImageData) ) {
result = false;
stop = true;
return;
}
if (handler) {
handler(depImageData, stop);
if ( stop )
stopped = true;
}
}
});
for (int i=0; !stopped && (i < _binaryData->dependentsArrayCount); ++i) {
if ( const dyld3::launch_cache::binary_format::Image* depImageData = needToProcess[i] ) {
Image depImage(depImageData);
if ( !depImage.recurseAllDependentImages(groups, allDependents, stopped, handler) ) {
return false;
}
}
}
return result;
}
bool Image::recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents,
void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const
{
bool stopped = false;
return recurseAllDependentImages(groups, allDependents, stopped, handler);
}
void Image::forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
{
assert(isDiskImage());
const uint32_t pageSize = (_binaryData->has16KBpages ? 0x4000 : 0x1000);
const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex);
const binary_format::DiskSegment* diskSegs = (binary_format::DiskSegment*)rawSegs;
uint32_t segIndex = 0;
uint32_t fileOffset = 0;
int64_t vmOffset = 0;
for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
const binary_format::DiskSegment* seg = &diskSegs[i];
if ( seg->filePageCount != 0 ) {
break;
}
vmOffset -= (uint64_t)seg->vmPageCount * pageSize;
}
for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
const binary_format::DiskSegment* seg = &diskSegs[i];
uint64_t vmSize = (uint64_t)seg->vmPageCount * pageSize;
uint32_t fileSize = seg->filePageCount * pageSize;
if ( !seg->paddingNotSeg ) {
bool stop = false;
handler(segIndex, ( fileSize == 0) ? 0 : fileOffset, fileSize, vmOffset, vmSize, seg->permissions, stop);
++segIndex;
if ( stop )
break;
}
vmOffset += vmSize;
fileOffset += fileSize;
}
}
void Image::forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
{
assert(!isDiskImage());
const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex);
const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)rawSegs;
bool stop = false;
for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) {
uint64_t vmOffset = cacheSegs[i].cacheOffset - cacheSegs[0].cacheOffset;
uint64_t vmSize = cacheSegs[i].size;
uint8_t permissions = cacheSegs[i].permissions;
handler(i, vmOffset, vmSize, permissions, stop);
if ( stop )
break;
}
}
bool Image::segmentHasFixups(uint32_t segIndex) const
{
return (segmentFixups(segIndex) != nullptr);
}
bool Image::containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions) const
{
if ( addr < imageLoadAddress )
return false;
__block bool found = false;
uint64_t offsetInImage = (char*)addr - (char*)imageLoadAddress;
if ( _binaryData->isDiskImage ) {
forEachDiskSegment(^(uint32_t segIterIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t segPerms, bool& stop) {
if ( (offsetInImage >= vmOffset) && (offsetInImage < vmOffset+vmSize) ) {
if ( permissions != nullptr )
*permissions = segPerms;
found = true;
stop = true;
}
});
}
else {
forEachCacheSegment(^(uint32_t segIterIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t segPerms, bool& stop) {
if ( (offsetInImage >= vmOffset) && (offsetInImage < vmOffset+vmSize) ) {
if ( permissions != nullptr )
*permissions = segPerms;
found = true;
stop = true;
}
});
}
return found;
}
void Image::forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const
{
const uint32_t initCount = _binaryData->initOffsetsArrayCount;
const uint32_t startIndex = _binaryData->initOffsetsArrayStartIndex;
const uint32_t* initOffsets = group().initializerOffsetsPool();
assert(startIndex + initCount <= group().initializerOffsetsCount());
for (uint32_t i=0; i < initCount; ++i) {
uint32_t anOffset = initOffsets[startIndex+i];
const void* func = (char*)imageLoadAddress + anOffset;
handler(func);
}
}
void Image::forEachInitBefore(void (^handler)(binary_format::ImageRef imageToInit)) const
{
const uint32_t initCount = _binaryData->initBeforeArrayCount;
const uint32_t startIndex = _binaryData->initBeforeArrayStartIndex;
const uint32_t endIndex = group().intializerListPoolCount();
const binary_format::ImageRef* initRefs = group().intializerListPool();
assert(startIndex + initCount <= endIndex);
for (uint32_t i=0; i < initCount; ++i) {
binary_format::ImageRef ref = initRefs[startIndex+i];
handler(ref);
}
}
void Image::forEachDOF(const void* imageLoadAddress, void (^handler)(const void* section)) const
{
const uint32_t dofCount = _binaryData->dofOffsetsArrayCount;
const uint32_t startIndex = _binaryData->dofOffsetsArrayStartIndex;
const uint32_t* dofOffsets = group().dofOffsetsPool();
assert(startIndex + dofCount <= group().dofOffsetsCount());
for (uint32_t i=0; i < dofCount; ++i) {
uint32_t anOffset = dofOffsets[startIndex+i];
const void* section = (char*)imageLoadAddress + anOffset;
handler(section);
}
}
Image Image::resolveImageRef(const ImageGroupList& groups, binary_format::ImageRef ref, bool applyOverrides)
{
__block binary_format::ImageRef targetRef = ref;
if ( applyOverrides ) {
binary_format::ImageRef refToMatch = ref;
refToMatch.clearKind();
for (int i=0; i < groups.count(); ++i) {
ImageGroup aGroup(groups[i]);
if ( aGroup.groupNum() >= 2 ) {
aGroup.forEachImageRefOverride(^(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool &stop) {
if ( refToMatch == standardDylibRef ) {
targetRef = overrideDylibRef;
stop = true;
}
});
}
}
}
for (int i=0; i < groups.count(); ++i) {
ImageGroup aGroup(groups[i]);
if ( aGroup.groupNum() == targetRef.groupNum() ) {
return aGroup.image(targetRef.indexInGroup());
}
}
return Image(nullptr);
}
void Image::forEachInitBefore(const ImageGroupList& groups, void (^handler)(Image imageToInit)) const
{
forEachInitBefore(^(binary_format::ImageRef ref) {
handler(resolveImageRef(groups, ref));
});
}
bool Image::validateUsingModTimeAndInode() const
{
return !group().binaryData()->imageFileInfoIsCdHash;
}
bool Image::validateUsingCdHash() const
{
if ( !group().binaryData()->imageFileInfoIsCdHash )
return false;
if ( !_binaryData->isDiskImage )
return false;
const binary_format::DiskImage* diskImage = asDiskImage();
if ( diskImage->codeSignFileOffset == 0 )
return false;
uint8_t zeros[16];
bzero(zeros, 16);
return (memcmp(cdHash16(), zeros, 16) != 0);
}
const uint8_t* Image::cdHash16() const
{
return _binaryData->fileInfo.cdHash16.bytes;
}
uint64_t Image::fileModTime() const
{
return _binaryData->fileInfo.statInfo.mtime;
}
uint64_t Image::fileINode() const
{
return _binaryData->fileInfo.statInfo.inode;
}
bool Image::isDiskImage() const
{
return _binaryData->isDiskImage;
}
const binary_format::DiskImage* Image::asDiskImage() const
{
assert(_binaryData->isDiskImage);
return (binary_format::DiskImage*)_binaryData;
}
const binary_format::CachedImage* Image::asCachedImage() const
{
assert(!_binaryData->isDiskImage);
return (binary_format::CachedImage*)_binaryData;
}
uint32_t Image::pageSize() const
{
return (_binaryData->has16KBpages ? 0x4000 : 0x1000);
}
uint32_t Image::cacheOffset() const
{
assert(!_binaryData->isDiskImage);
const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex);
const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)rawSegs;
return cacheSegs[0].cacheOffset;
}
uint32_t Image::patchStartIndex() const
{
return asCachedImage()->patchStartIndex;
}
uint32_t Image::patchCount() const
{
return asCachedImage()->patchCount;
}
uint64_t Image::sliceOffsetInFile() const
{
return asDiskImage()->sliceOffsetIn4K * 4096;
}
bool Image::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
{
const binary_format::DiskImage* diskImage = asDiskImage();
if ( diskImage->codeSignFileOffset != 0 ) {
fileOffset = diskImage->codeSignFileOffset;
size = diskImage->codeSignFileSize;
return true;
}
return false;
}
bool Image::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
{
const binary_format::DiskImage* diskImage = asDiskImage();
if ( diskImage->fairPlayTextPageCount != 0 ) {
textOffset = diskImage->fairPlayTextStartPage * pageSize();
size = diskImage->fairPlayTextPageCount * pageSize();
return true;
}
return false;
}
uint64_t Image::vmSizeToMap() const
{
return asDiskImage()->totalVmPages * pageSize();
}
void Image::forEachFixup(const uint8_t* pageFixups, const void* segContent, uint32_t& offset, uint32_t& ordinal,
void (^handler)(uint32_t pageOffset, FixupKind kind, uint32_t ordinal, bool& stop))
{
bool stop = false;
for (const uint8_t* p = pageFixups; (*p != 0) && !stop;) {
binary_format::FixUpOpcode fullOp = (binary_format::FixUpOpcode)(*p);
binary_format::FixUpOpcode majorOp = (binary_format::FixUpOpcode)(*p & 0xF0);
uint8_t low4 = (*p & 0x0F);
switch ( majorOp ) {
case binary_format::FixUpOpcode::done:
return;
case binary_format::FixUpOpcode::rebase32: switch ( fullOp ) {
case binary_format::FixUpOpcode::bind64:
handler(offset, FixupKind::bind64, ordinal, stop);
offset += 8;
++p;
break;
case binary_format::FixUpOpcode::bind32:
handler(offset, FixupKind::bind32, ordinal, stop);
offset += 4;
++p;
break;
case binary_format::FixUpOpcode::rebase64:
handler(offset, FixupKind::rebase64, 0, stop);
offset += 8;
++p;
break;
case binary_format::FixUpOpcode::rebase32:
handler(offset, FixupKind::rebase32, 0, stop);
offset += 4;
++p;
break;
case binary_format::FixUpOpcode::rebaseText32:
handler(offset, FixupKind::rebaseText32, 0, stop);
offset += 4;
++p;
break;
case binary_format::FixUpOpcode::bindText32:
handler(offset, FixupKind::bindText32, ordinal, stop);
offset += 4;
++p;
break;
case binary_format::FixUpOpcode::bindTextRel32:
handler(offset, FixupKind::bindTextRel32, ordinal, stop);
offset += 4;
++p;
break;
case binary_format::FixUpOpcode::bindImportJmp32:
handler(offset, FixupKind::bindImportJmp32, ordinal, stop);
offset += 5;
++p;
break;
default:
assert(0 && "bad opcode");
break;
}
break;
case binary_format::FixUpOpcode::incPageOffset:
if ( low4 == 0 ) {
++p;
offset += read_uleb128(p, p+8)*4;
}
else {
offset += (low4*4);
++p;
}
break;
case binary_format::FixUpOpcode::setPageOffset:
if ( low4 == 0 ) {
++p;
offset = (uint32_t)read_uleb128(p, p+8);
}
else {
offset = low4;
++p;
}
break;
case binary_format::FixUpOpcode::incOrdinal:
if ( low4 == 0 ) {
++p;
ordinal += read_uleb128(p, p+8);
}
else {
ordinal += low4;
++p;
}
break;
case binary_format::FixUpOpcode::setOrdinal:
if ( low4 == 0 ) {
++p;
ordinal = (uint32_t)read_uleb128(p, p+8);
}
else {
ordinal = low4;
++p;
}
break;
case binary_format::FixUpOpcode::repeat: {
++p;
uint32_t count = (uint32_t)read_uleb128(p, p+8);
uint8_t pattern[32];
for (int j=0; j < low4; ++j) {
pattern[j] = *p++;
}
pattern[low4] = (uint8_t)binary_format::FixUpOpcode::done;
for (int j=0; j < count; ++j) {
forEachFixup(&pattern[0], segContent, offset, ordinal, handler);
if ( stop )
break;
}
}
break;
default:
assert(0 && "bad opcode");
break;
}
}
}
const binary_format::SegmentFixupsByPage* Image::segmentFixups(uint32_t segIndex) const
{
const binary_format::DiskImage* diskImage = asDiskImage();
uint32_t segCountWithFixups = diskImage->fixupsPoolSegCount;
const binary_format::AllFixupsBySegment* allFixups = group().fixUps(diskImage->fixupsPoolOffset);
for (uint32_t i=0; i < segCountWithFixups; ++i) {
if ( allFixups[i].segIndex == segIndex ) {
return (binary_format::SegmentFixupsByPage*)((char*)allFixups + allFixups[i].offset);
}
}
return nullptr;
}
void Image::forEachFixup(uint32_t segIndex, MemoryRange segContent, void (^handler)(uint64_t segOffset, FixupKind, TargetSymbolValue, bool& stop)) const
{
const binary_format::SegmentFixupsByPage* segFixups = segmentFixups(segIndex);
if ( segFixups == nullptr )
return;
assert(segFixups->pageCount*segFixups->pageSize <= segContent.size);
const uint32_t ordinalsIndexInGroupPool = asDiskImage()->targetsArrayStartIndex;
const uint32_t maxOrdinal = asDiskImage()->targetsArrayCount;
const TargetSymbolValue* groupArray = group().targetValuesArray();
assert(ordinalsIndexInGroupPool < group().targetValuesCount());
const TargetSymbolValue* targetOrdinalArray = &groupArray[ordinalsIndexInGroupPool];
for (uint32_t pageIndex=0; pageIndex < segFixups->pageCount; ++pageIndex) {
const uint8_t* opcodes = (uint8_t*)(segFixups) + segFixups->pageInfoOffsets[pageIndex];
uint64_t pageStartOffet = pageIndex * segFixups->pageSize;
uint32_t curOffset = 0;
uint32_t curOrdinal = 0;
forEachFixup(opcodes, segContent.address, curOffset, curOrdinal, ^(uint32_t pageOffset, FixupKind kind, uint32_t targetOrdinal, bool& stop) {
assert(targetOrdinal < maxOrdinal);
handler(pageStartOffet + pageOffset, kind, targetOrdinalArray[targetOrdinal], stop);
});
}
}
} }