#include <stdint.h>
#include <assert.h>
#include <uuid/uuid.h>
#include <unistd.h>
#include <limits.h>
#include <mach-o/dyld_priv.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <System/machine/cpu_capabilities.h>
#include <_simple.h>
extern "C" {
#include <corecrypto/ccdigest.h>
#include <corecrypto/ccsha2.h>
}
#include "Closure.h"
#include "MachOFile.h"
#include "MachOLoaded.h"
#include "StringUtils.h"
#include "objc-shared-cache.h"
namespace dyld3 {
namespace closure {
const void* TypedBytes::payload() const
{
return (uint8_t*)this + sizeof(TypedBytes);
}
void* TypedBytes::payload()
{
return (uint8_t*)this + sizeof(TypedBytes);
}
const TypedBytes* ContainerTypedBytes::first() const
{
return (TypedBytes*)payload();
}
const TypedBytes* ContainerTypedBytes::next(const TypedBytes* p) const
{
assert((p->payloadLength & 0x3) == 0);
return (TypedBytes*)((uint8_t*)(p->payload()) + p->payloadLength);
}
void ContainerTypedBytes::forEachAttribute(void (^handler)(const TypedBytes* typedBytes, bool& stop)) const
{
assert(((long)this & 0x3) == 0);
const TypedBytes* end = next(this);
bool stop = false;
for (const TypedBytes* p = first(); p < end && !stop; p = next(p)) {
handler(p, stop);
}
}
void ContainerTypedBytes::forEachAttributePayload(Type requestedType, void (^handler)(const void* payload, uint32_t size, bool& stop)) const
{
forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
if ( (Type)(typedBytes->type) != requestedType )
return;
handler(typedBytes->payload(), typedBytes->payloadLength, stop);
});
}
const void* ContainerTypedBytes::findAttributePayload(Type requestedType, uint32_t* payloadSize) const
{
assert(((long)this & 0x3) == 0);
if ( payloadSize != nullptr )
*payloadSize = 0;
const TypedBytes* end = next(this);
bool stop = false;
for (const TypedBytes* p = first(); p < end && !stop; p = next(p)) {
if ( (Type)(p->type) == requestedType ) {
if ( payloadSize != nullptr )
*payloadSize = p->payloadLength;
return p->payload();
}
}
return nullptr;
}
const Image::Flags& Image::getFlags() const
{
return *(Flags*)((uint8_t*)this + 2*sizeof(TypedBytes));
}
bool Image::isInvalid() const
{
return getFlags().isInvalid;
}
size_t Image::size() const
{
return sizeof(TypedBytes) + this->payloadLength;
}
ImageNum Image::imageNum() const
{
return getFlags().imageNum;
}
bool Image::representsImageNum(ImageNum num) const
{
const Flags& flags = getFlags();
if ( flags.imageNum == num )
return true;
if ( !flags.isDylib )
return false;
if ( !flags.hasOverrideImageNum )
return false;
ImageNum cacheImageNum;
if ( isOverrideOfDyldCacheImage(cacheImageNum) )
return (cacheImageNum == num);
return false;
}
uint32_t Image::maxLoadCount() const
{
return getFlags().maxLoadCount;
}
bool Image::isBundle() const
{
return getFlags().isBundle;
}
bool Image::isDylib() const
{
return getFlags().isDylib;
}
bool Image::isExecutable() const
{
return getFlags().isExecutable;
}
bool Image::hasObjC() const
{
return getFlags().hasObjC;
}
bool Image::is64() const
{
return getFlags().is64;
}
bool Image::hasWeakDefs() const
{
return getFlags().hasWeakDefs;
}
bool Image::mayHavePlusLoads() const
{
return getFlags().mayHavePlusLoads;
}
bool Image::neverUnload() const
{
return getFlags().neverUnload;
}
bool Image::overridableDylib() const
{
return getFlags().overridableDylib;
}
bool Image::inDyldCache() const
{
return getFlags().inDyldCache;
}
const char* Image::path() const
{
const PathAndHash* result = (PathAndHash*)findAttributePayload(Type::pathWithHash);
assert(result && "Image missing pathWithHash");
return result->path;
}
const char* Image::leafName() const
{
uint32_t size;
const PathAndHash* result = (PathAndHash*)findAttributePayload(Type::pathWithHash, &size);
assert(result && "Image missing pathWithHash");
for (const char* p=(char*)result + size; p > result->path; --p) {
if ( *p == '/' )
return p+1;
}
return result->path;
}
bool Image::hasFileModTimeAndInode(uint64_t& inode, uint64_t& mTime) const
{
uint32_t size;
const FileInfo* info = (FileInfo*)(findAttributePayload(Type::fileInodeAndTime, &size));
if ( info != nullptr ) {
assert(size == sizeof(FileInfo));
inode = info->inode;
mTime = info->modTime;
return true;
}
return false;
}
void Image::forEachCDHash(void (^handler)(const uint8_t cdHash[20], bool& stop)) const
{
forEachAttribute(^(const TypedBytes* typedBytes, bool& stopLoop) {
if ( (Type)(typedBytes->type) != Type::cdHash )
return;
assert(typedBytes->payloadLength == 20);
const uint8_t* bytes = (const uint8_t*)typedBytes->payload();
handler(bytes, stopLoop);
});
}
bool Image::getUuid(uuid_t uuid) const
{
uint32_t size;
const uint8_t* bytes = (uint8_t*)(findAttributePayload(Type::uuid, &size));
if ( bytes == nullptr )
return false;
assert(size == 16);
memcpy(uuid, bytes, 16);
return true;
}
bool Image::hasCodeSignature(uint32_t& sigFileOffset, uint32_t& sigSize) const
{
uint32_t sz;
const Image::CodeSignatureLocation* sigInfo = (Image::CodeSignatureLocation*)(findAttributePayload(Type::codeSignLoc, &sz));
if ( sigInfo != nullptr ) {
assert(sz == sizeof(Image::CodeSignatureLocation));
sigFileOffset = sigInfo->fileOffset;
sigSize = sigInfo->fileSize;
return true;
}
return false;
}
bool Image::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
{
uint32_t sz;
const Image::FairPlayRange* fpInfo = (Image::FairPlayRange*)(findAttributePayload(Type::fairPlayLoc, &sz));
if ( fpInfo != nullptr ) {
assert(sz == sizeof(Image::FairPlayRange));
textOffset = fpInfo->rangeStart;
size = fpInfo->rangeLength;
return true;
}
return false;
}
const Array<Image::LinkedImage> Image::dependentsArray() const
{
uint32_t size;
LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
assert((size % sizeof(LinkedImage)) == 0);
uintptr_t count = size / sizeof(LinkedImage);
return Array<Image::LinkedImage>(dependents, count, count);
}
void Image::forEachDependentImage(void (^handler)(uint32_t dependentIndex, LinkKind kind, ImageNum imageNum, bool& stop)) const
{
uint32_t size;
const LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
assert((size % sizeof(LinkedImage)) == 0);
const uint32_t count = size / sizeof(LinkedImage);
bool stop = false;
for (uint32_t i=0; (i < count) && !stop; ++i) {
LinkKind kind = dependents[i].kind();
ImageNum imageNum = dependents[i].imageNum();
if ( (imageNum == kMissingWeakLinkedImage) && (kind == LinkKind::weak) )
continue;
handler(i, kind, imageNum, stop);
}
}
ImageNum Image::dependentImageNum(uint32_t depIndex) const
{
uint32_t size;
const LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
assert((size % sizeof(LinkedImage)) == 0);
const uint32_t count = size / sizeof(LinkedImage);
assert(depIndex < count);
return dependents[depIndex].imageNum();
}
uint32_t Image::hashFunction(const char* str)
{
uint32_t h = 0;
for (const char* s=str; *s != '\0'; ++s)
h = h*5 + *s;
return h;
}
void Image::forEachAlias(void (^handler)(const char* aliasPath, bool& stop)) const
{
__block bool foundFirst = false;
forEachAttribute(^(const TypedBytes* typedBytes, bool& stopLoop) {
if ( (Type)(typedBytes->type) != Type::pathWithHash )
return;
if ( foundFirst ) {
const PathAndHash* aliasInfo = (PathAndHash*)typedBytes->payload();
handler(aliasInfo->path, stopLoop);
}
else {
foundFirst = true;
}
});
}
bool Image::hasPathWithHash(const char* path, uint32_t hash) const
{
__block bool found = false;
forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
if ( (Type)(typedBytes->type) != Type::pathWithHash )
return;
const PathAndHash* pathInfo = (PathAndHash*)typedBytes->payload();
if ( (pathInfo->hash == hash) && (strcmp(path, pathInfo->path) == 0) ) {
stop = true;
found = true;
}
});
return found;
}
void Image::forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize,
uint8_t permissions, bool laterReadOnly, bool& stop)) const
{
uint32_t size;
const DiskSegment* segments = (DiskSegment*)findAttributePayload(Type::diskSegment, &size);
assert(segments != nullptr);
assert((size % sizeof(DiskSegment)) == 0);
const uint32_t count = size / sizeof(DiskSegment);
const uint32_t pageSz = pageSize();
uint32_t segIndex = 0;
uint32_t fileOffset = 0;
int64_t vmOffset = 0;
for (uint32_t i=0; i < count; ++i) {
const DiskSegment* seg = &segments[i];
if ( seg->filePageCount != 0 ) {
break;
}
vmOffset -= (uint64_t)seg->vmPageCount * pageSz;
}
bool stop = false;
for (uint32_t i=0; i < count && !stop; ++i) {
const DiskSegment* seg = &segments[i];
uint64_t vmSize = (uint64_t)seg->vmPageCount * pageSz;
uint32_t fileSize = seg->filePageCount * pageSz;
if ( !seg->paddingNotSeg ) {
uint8_t perms = seg->permissions;
bool laterRO = false;
if ( perms == Image::DiskSegment::kReadOnlyDataPermissions ) {
perms = VM_PROT_READ|VM_PROT_WRITE;
laterRO = true;
}
handler(segIndex, ( fileSize == 0) ? 0 : fileOffset, fileSize, vmOffset, vmSize, perms, laterRO, stop);
++segIndex;
}
vmOffset += vmSize;
fileOffset += fileSize;
}
}
uint32_t Image::pageSize() const
{
if ( getFlags().has16KBpages )
return 0x4000;
else
return 0x1000;
}
uint32_t Image::cacheOffset() const
{
uint32_t size;
const DyldCacheSegment* segments = (DyldCacheSegment*)findAttributePayload(Type::cacheSegment, &size);
assert(segments != nullptr);
assert((size % sizeof(DyldCacheSegment)) == 0);
return segments[0].cacheOffset;
}
void Image::forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
{
uint32_t size;
const DyldCacheSegment* segments = (DyldCacheSegment*)findAttributePayload(Type::cacheSegment, &size);
assert(segments != nullptr);
assert((size % sizeof(DyldCacheSegment)) == 0);
const uint32_t count = size / sizeof(DyldCacheSegment);
bool stop = false;
for (uint32_t i=0; i < count; ++i) {
uint64_t vmOffset = segments[i].cacheOffset - segments[0].cacheOffset;
uint64_t vmSize = segments[i].size;
uint8_t permissions = segments[i].permissions;
handler(i, vmOffset, vmSize, permissions, stop);
if ( stop )
break;
}
}
uint64_t Image::textSize() const
{
__block uint64_t result = 0;
if ( inDyldCache() ) {
forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
result = vmSize;
stop = true;
});
}
else {
forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& stop) {
if ( permissions != 0) {
result = vmSize;
stop = true;
}
});
}
return result;
}
bool Image::containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permsResult) const
{
__block bool result = false;
uint64_t targetAddr = (uint64_t)addr;
uint64_t imageStart = (uint64_t)imageLoadAddress;
if ( inDyldCache() ) {
forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
if ( (targetAddr >= imageStart+vmOffset) && (targetAddr < imageStart+vmOffset+vmSize) ) {
result = true;
if ( permsResult )
*permsResult = permissions;
stop = true;
}
});
}
else {
forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& stop) {
if ( (targetAddr >= imageStart+vmOffset) && (targetAddr < imageStart+vmOffset+vmSize) ) {
result = true;
if ( permsResult )
*permsResult = permissions;
stop = true;
}
});
}
return result;
}
uint64_t Image::vmSizeToMap() const
{
uint32_t size;
const Image::MappingInfo* info = (Image::MappingInfo*)(findAttributePayload(Type::mappingInfo, &size));
assert(info != nullptr);
assert(size == sizeof(Image::MappingInfo));
return info->totalVmPages * pageSize();
}
uint64_t Image::sliceOffsetInFile() const
{
uint32_t size;
const Image::MappingInfo* info = (Image::MappingInfo*)(findAttributePayload(Type::mappingInfo, &size));
assert(info != nullptr);
assert(size == sizeof(Image::MappingInfo));
return info->sliceOffsetIn4K * 0x1000;
}
void Image::forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const
{
uint32_t size;
const uint32_t* inits = (uint32_t*)findAttributePayload(Type::initOffsets, &size);
if ( inits != nullptr ) {
assert((size % sizeof(uint32_t)) == 0);
const uint32_t count = size / sizeof(uint32_t);
for (uint32_t i=0; i < count; ++i) {
uint32_t offset = inits[i];
const void* init = (void*)((uint8_t*)imageLoadAddress + offset);
handler(init);
}
return;
}
const Image::InitializerSectionRange* range = (Image::InitializerSectionRange*)findAttributePayload(Type::initsSection, &size);
if ( range != nullptr ) {
const uint32_t pointerSize = is64() ? 8 : 4;
const uint32_t* start32 = (uint32_t*)((uint8_t*)imageLoadAddress + range->sectionOffset);
const uint64_t* start64 = (uint64_t*)((uint8_t*)imageLoadAddress + range->sectionOffset);
const uint32_t count = range->sectionSize / pointerSize;
for (uint32_t i=0; i < count; ++i) {
if ( pointerSize == 8 )
handler((void*)(long)(start64[i]));
else
handler((void*)(long)(start32[i]));
}
}
}
bool Image::forEachInitializerSection(void (^handler)(uint32_t sectionOffset, uint32_t sectionSize)) const
{
__block bool result = false;
forEachAttributePayload(Type::initsSection, ^(const void* payload, uint32_t size, bool& stop) {
const Image::InitializerSectionRange* range = (Image::InitializerSectionRange*)payload;
assert((size % sizeof(Image::InitializerSectionRange)) == 0);
handler(range->sectionOffset, range->sectionSize);
result = true;
});
return result;
}
bool Image::hasInitializers() const
{
uint32_t size;
return ( findAttributePayload(Type::initOffsets, &size) != nullptr );
}
bool Image::hasTerminators() const
{
return getFlags().hasTerminators;
}
bool Image::hasReadOnlyData() const
{
return getFlags().hasReadOnlyData;
}
bool Image::hasChainedFixups() const
{
return getFlags().hasChainedFixups;
}
bool Image::hasPrecomputedObjC() const
{
return getFlags().hasPrecomputedObjC;
}
bool Image::fixupsNotEncoded() const
{
return getFlags().fixupsNotEncoded;
}
bool Image::rebasesNotEncoded() const
{
return getFlags().rebasesNotEncoded;
}
void Image::forEachTerminator(const void* imageLoadAddress, void (^handler)(const void* terminator)) const
{
uint32_t size;
const uint32_t* terms = (uint32_t*)findAttributePayload(Type::termOffsets, &size);
if ( terms != nullptr ) {
assert((size % sizeof(uint32_t)) == 0);
const uint32_t count = size / sizeof(uint32_t);
for (uint32_t i=0; i < count; ++i) {
uint32_t offset = terms[i];
const void* term = (void*)((uint8_t*)imageLoadAddress + offset);
handler(term);
}
}
}
void Image::forEachDOF(const void* imageLoadAddress, void (^handler)(const void* dofSection)) const
{
uint32_t size;
const uint32_t* dofs = (uint32_t*)findAttributePayload(Type::dofOffsets, &size);
if ( dofs != nullptr ) {
assert((size % sizeof(uint32_t)) == 0);
const uint32_t count = size / sizeof(uint32_t);
for (uint32_t i=0; i < count; ++i) {
uint32_t offset = dofs[i];
const void* sect = (void*)((uint8_t*)imageLoadAddress + offset);
handler(sect);
}
}
}
void Image::forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop),
void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop),
void (^chainedFixups)(uint64_t imageOffsetToStarts, const Array<ResolvedSymbolTarget>& targets, bool& stop),
void (^fixupObjCImageInfo)(uint64_t imageOffsetToFixup),
void (^fixupObjCProtocol)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop),
void (^fixupObjCSelRef)(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool& stop),
void (^fixupObjCStableSwift)(uint64_t imageOffsetToFixup, bool& stop),
void (^fixupObjCMethodList)(uint64_t imageOffsetToFixup, bool& stop)) const
{
const uint32_t pointerSize = is64() ? 8 : 4;
uint64_t curRebaseOffset = 0;
bool stop = false;
for (const Image::RebasePattern& rebasePat : rebaseFixups()) {
if ( rebasePat.contigCount == 0 ) {
if ( (rebasePat.repeatCount == 0) && (rebasePat.skipCount == 0) ) {
curRebaseOffset = 0;
}
else {
curRebaseOffset += rebasePat.repeatCount * rebasePat.skipCount;
}
}
else {
for (int r=0; r < rebasePat.repeatCount && !stop; ++r) {
for (int i=0; i < rebasePat.contigCount && !stop; ++i) {
rebase(curRebaseOffset, stop);
curRebaseOffset += pointerSize;
}
curRebaseOffset += pointerSize * rebasePat.skipCount;
}
}
if ( stop )
break;
}
if ( stop )
return;
stop = this->forEachBind(bind);
if ( stop )
return;
if (hasChainedFixups())
chainedFixups(chainedStartsOffset(), chainedTargets(), stop);
if ( hasPrecomputedObjC() ) {
ResolvedSymbolTarget objcProtocolClassTarget;
uint64_t objcImageInfoVMOffset = 0;
Array<ProtocolISAFixup> protocolISAFixups;
Array<Image::SelectorReferenceFixup> selRefFixupEntries;
Array<Image::ClassStableSwiftFixup> classStableSwiftFixups;
Array<Image::MethodListFixup> methodListFixups;
objcFixups(objcProtocolClassTarget, objcImageInfoVMOffset, protocolISAFixups,
selRefFixupEntries, classStableSwiftFixups, methodListFixups);
fixupObjCImageInfo(objcImageInfoVMOffset);
for (const Image::ProtocolISAFixup& bindPat : protocolISAFixups) {
uint64_t curBindOffset = bindPat.startVmOffset;
for (uint16_t i=0; i < bindPat.repeatCount; ++i) {
fixupObjCProtocol(curBindOffset, objcProtocolClassTarget, stop);
curBindOffset += (pointerSize * (1 + bindPat.skipCount));
if ( stop )
break;
}
if ( stop )
break;
}
for (uintptr_t i = 0, e = selRefFixupEntries.count(); i != e; ++i) {
Image::SelectorReferenceFixup fixupEntry = selRefFixupEntries[i];
uint64_t curFixupOffset = fixupEntry.chainStartVMOffset;
while (i != e) {
fixupEntry = selRefFixupEntries[++i];
fixupObjCSelRef(curFixupOffset, fixupEntry.chainEntry.index, fixupEntry.chainEntry.inSharedCache, stop);
if ( stop )
break;
if ( fixupEntry.chainEntry.next == 0 )
break;
curFixupOffset += (4 * fixupEntry.chainEntry.next);
}
}
for (const Image::ClassStableSwiftFixup& bindPat : classStableSwiftFixups) {
uint64_t curBindOffset = bindPat.startVmOffset;
for (uint16_t i=0; i < bindPat.repeatCount; ++i) {
fixupObjCStableSwift(curBindOffset, stop);
curBindOffset += (pointerSize * (1 + bindPat.skipCount));
if ( stop )
break;
}
if ( stop )
break;
}
for (const Image::MethodListFixup& bindPat : methodListFixups) {
uint64_t curBindOffset = bindPat.startVmOffset;
for (uint16_t i=0; i < bindPat.repeatCount; ++i) {
fixupObjCMethodList(curBindOffset, stop);
curBindOffset += (pointerSize * (1 + bindPat.skipCount));
if ( stop )
break;
}
if ( stop )
break;
}
}
}
bool Image::forEachBind(void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const
{
const uint32_t pointerSize = is64() ? 8 : 4;
bool stop = false;
for (const Image::BindPattern& bindPat : bindFixups()) {
uint64_t curBindOffset = bindPat.startVmOffset;
for (uint16_t i=0; i < bindPat.repeatCount; ++i) {
bind(curBindOffset, bindPat.target, stop);
curBindOffset += (pointerSize * (1 + bindPat.skipCount));
if ( stop )
break;
}
if ( stop )
break;
}
return stop;
}
void Image::forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop),
void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const
{
bool stop = false;
const Array<Image::TextFixupPattern> f = textFixups();
for (const Image::TextFixupPattern& pat : f) {
uint32_t curOffset = pat.startVmOffset;
for (uint16_t i=0; i < pat.repeatCount; ++i) {
if ( pat.target.raw == 0 )
rebase(curOffset, stop);
else
bind(curOffset, pat.target, stop);
curOffset += pat.skipCount;
}
}
}
const Array<Image::RebasePattern> Image::rebaseFixups() const
{
uint32_t rebaseFixupsSize;
Image::RebasePattern* rebaseFixupsContent = (RebasePattern*)findAttributePayload(Type::rebaseFixups, &rebaseFixupsSize);
uint32_t rebaseCount = rebaseFixupsSize/sizeof(RebasePattern);
return Array<RebasePattern>(rebaseFixupsContent, rebaseCount, rebaseCount);
}
const Array<Image::BindPattern> Image::bindFixups() const
{
uint32_t bindFixupsSize;
BindPattern* bindFixupsContent = (BindPattern*)findAttributePayload(Type::bindFixups, &bindFixupsSize);
uint32_t bindCount = bindFixupsSize/sizeof(BindPattern);
return Array<BindPattern>(bindFixupsContent, bindCount, bindCount);
}
uint64_t Image::chainedStartsOffset() const
{
uint32_t size;
uint64_t* startsOffset = (uint64_t*)findAttributePayload(Type::chainedStartsOffset, &size);
if ( startsOffset == nullptr )
return 0; assert(size == sizeof(uint64_t));
return *startsOffset;
}
void Image::objcFixups(ResolvedSymbolTarget& objcProtocolClassTarget,
uint64_t& objcImageInfoVMOffset,
Array<ProtocolISAFixup>& protocolISAFixups,
Array<SelectorReferenceFixup>& selRefFixups,
Array<ClassStableSwiftFixup>& classStableSwiftFixups,
Array<MethodListFixup>& methodListFixups) const
{
if (!hasPrecomputedObjC())
return;
uint32_t contentSize;
const uint8_t* fixupsContent = (uint8_t*)findAttributePayload(Type::objcFixups, &contentSize);
const uint8_t* fixupsContentEnd = fixupsContent + contentSize;
uint32_t protocolFixupCount = 0;
uint32_t selRefFixupCount = 0;
memcpy(&objcProtocolClassTarget, fixupsContent, sizeof(ResolvedSymbolTarget));
fixupsContent += sizeof(ResolvedSymbolTarget);
memcpy(&objcImageInfoVMOffset, fixupsContent, sizeof(uint64_t));
fixupsContent += sizeof(uint64_t);
memcpy(&protocolFixupCount, fixupsContent, sizeof(uint32_t));
fixupsContent += sizeof(uint32_t);
memcpy(&selRefFixupCount, fixupsContent, sizeof(uint32_t));
fixupsContent += sizeof(uint32_t);
if ( protocolFixupCount != 0) {
protocolISAFixups = Array<ProtocolISAFixup>((ProtocolISAFixup*)fixupsContent, protocolFixupCount, protocolFixupCount);
fixupsContent += (sizeof(ProtocolISAFixup) * protocolFixupCount);
}
if ( selRefFixupCount != 0) {
selRefFixups = Array<SelectorReferenceFixup>((SelectorReferenceFixup*)fixupsContent, selRefFixupCount, selRefFixupCount);
fixupsContent += (sizeof(SelectorReferenceFixup) * selRefFixupCount);
}
if (fixupsContent == fixupsContentEnd)
return;
uint32_t stableSwiftFixupCount = 0;
uint32_t methodListFixupCount = 0;
memcpy(&stableSwiftFixupCount, fixupsContent, sizeof(uint32_t));
fixupsContent += sizeof(uint32_t);
memcpy(&methodListFixupCount, fixupsContent, sizeof(uint32_t));
fixupsContent += sizeof(uint32_t);
if ( stableSwiftFixupCount != 0) {
classStableSwiftFixups = Array<ClassStableSwiftFixup>((ClassStableSwiftFixup*)fixupsContent, stableSwiftFixupCount, stableSwiftFixupCount);
fixupsContent += (sizeof(ClassStableSwiftFixup) * stableSwiftFixupCount);
}
if ( methodListFixupCount != 0) {
methodListFixups = Array<MethodListFixup>((MethodListFixup*)fixupsContent, methodListFixupCount, methodListFixupCount);
fixupsContent += (sizeof(MethodListFixup) * methodListFixupCount);
}
}
const Array<Image::ResolvedSymbolTarget> Image::chainedTargets() const
{
uint32_t size;
ResolvedSymbolTarget* targetsContent = (ResolvedSymbolTarget*)findAttributePayload(Type::chainedFixupsTargets, &size);
uint32_t count = size/sizeof(ResolvedSymbolTarget);
return Array<ResolvedSymbolTarget>(targetsContent, count, count);
}
const Array<Image::TextFixupPattern> Image::textFixups() const
{
uint32_t fixupsSize;
TextFixupPattern* fixupsContent = (TextFixupPattern*)findAttributePayload(Type::textFixups, &fixupsSize);
uint32_t count = fixupsSize/sizeof(TextFixupPattern);
return Array<TextFixupPattern>(fixupsContent, count, count);
}
bool Image::isOverrideOfDyldCacheImage(ImageNum& imageNum) const
{
uint32_t size;
const uint32_t* content = (uint32_t*)findAttributePayload(Type::imageOverride, &size);
if ( content != nullptr ) {
assert(size == sizeof(uint32_t));
imageNum = *content;
return true;
}
return false;
}
void Image::forEachImageToInitBefore(void (^handler)(ImageNum imageToInit, bool& stop)) const
{
uint32_t size;
const ImageNum* initBefores = (ImageNum*)findAttributePayload(Type::initBefores, &size);
if ( initBefores != nullptr ) {
assert((size % sizeof(ImageNum)) == 0);
const uint32_t count = size / sizeof(ImageNum);
bool stop = false;
for (uint32_t i=0; (i < count) && !stop; ++i) {
handler(initBefores[i], stop);
}
}
}
const char* Image::variantString() const
{
return (this->fixupsNotEncoded() ? "minimal" : "full");
}
size_t ImageArray::size() const
{
return sizeof(TypedBytes) + this->payloadLength;
}
size_t ImageArray::startImageNum() const
{
return firstImageNum;
}
uint32_t ImageArray::imageCount() const
{
return count;
}
void ImageArray::forEachImage(void (^callback)(const Image* image, bool& stop)) const
{
bool stop = false;
for (uint32_t i=0; i < count && !stop; ++i) {
const Image* image = (Image*)((uint8_t*)payload() + offsets[i]);
callback(image, stop);
if (stop)
break;
}
}
bool ImageArray::hasPath(const char* path, ImageNum& num) const
{
const uint32_t hash = Image::hashFunction(path);
__block bool found = false;
forEachImage(^(const Image* image, bool& stop) {
if ( image->hasPathWithHash(path, hash) ) {
num = image->imageNum();
found = true;
stop = true;
}
});
return found;
}
const Image* ImageArray::imageForNum(ImageNum num) const
{
if (hasRoots) {
__block const Image* foundImage = nullptr;
forEachImage(^(const Image *image, bool &stop) {
if (image->imageNum() == num) {
foundImage = image;
stop = true;
}
});
return foundImage;
}
if ( num < firstImageNum )
return nullptr;
uint32_t index = num - firstImageNum;
if ( index >= count )
return nullptr;
return (Image*)((uint8_t*)payload() + offsets[index]);
}
const Image* ImageArray::findImage(const Array<const ImageArray*> imagesArrays, ImageNum imageNum)
{
for (uintptr_t index = imagesArrays.count(); index > 0; --index) {
const ImageArray* ia = imagesArrays[index - 1];
if ( const Image* result = ia->imageForNum(imageNum) )
return result;
}
return nullptr;
}
void ImageArray::deallocate() const
{
::vm_deallocate(mach_task_self(), (long)this, size());
}
size_t Closure::size() const
{
return sizeof(TypedBytes) + this->payloadLength;
}
const ImageArray* Closure::images() const
{
__block const TypedBytes* result = nullptr;
forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
if ( (Type)(typedBytes->type) == Type::imageArray ) {
result = typedBytes;
stop = true;
}
});
return (ImageArray*)result;
}
ImageNum Closure::topImageNum() const
{
uint32_t size;
const ImageNum* top = (ImageNum*)findAttributePayload(Type::topImage, &size);
assert(top != nullptr);
assert(size == sizeof(ImageNum));
return *top;
}
const Image* Closure::topImage() const
{
ImageNum imageNum = this->topImageNum();
const dyld3::closure::ImageArray* closureImages = this->images();
return closureImages->imageForNum(imageNum);
}
void Closure::forEachPatchEntry(void (^handler)(const PatchEntry& entry)) const
{
forEachAttributePayload(Type::cacheOverrides, ^(const void* payload, uint32_t size, bool& stop) {
assert((size % sizeof(Closure::PatchEntry)) == 0);
const PatchEntry* patches = (PatchEntry*)payload;
const PatchEntry* patchesEnd = (PatchEntry*)((char*)payload + size);
for (const PatchEntry* p=patches; p < patchesEnd; ++p)
handler(*p);
});
}
void Closure::forEachWarning(Closure::Warning::Type type, void (^handler)(const char* warning, bool& stop)) const
{
forEachAttributePayload(Type::warning, ^(const void* payload, uint32_t size, bool& stop) {
const Closure::Warning* warning = (const Closure::Warning*)payload;
if ( warning->type != type )
return;
handler(warning->message, stop);
});
}
void Closure::deallocate() const
{
::vm_deallocate(mach_task_self(), (long)this, size());
}
void LaunchClosure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const
{
uint32_t size;
const char* paths = (const char*)findAttributePayload(Type::missingFiles, &size);
bool stop = false;
for (const char* s=paths; s < &paths[size]; ++s) {
if ( *s != '\0' )
handler(s, stop);
if ( stop )
break;
s += strlen(s);
}
}
void LaunchClosure::forEachSkipIfExistsFile(void (^handler)(const SkippedFile& file, bool& stop)) const
{
uint32_t size;
const uint64_t* files = (const uint64_t*)findAttributePayload(Type::existingFiles, &size);
if (files == nullptr)
return;
uint64_t fileCount = *files++;
const char* paths = (const char*)(files + (2 * fileCount));
bool stop = false;
for (const char* s=paths; s < &paths[size]; ++s) {
if ( *s != '\0' ) {
uint64_t inode = *files++;
uint64_t mtime = *files++;
SkippedFile skippedFile = { s, inode, mtime };
handler(skippedFile, stop);
}
if ( stop )
break;
s += strlen(s);
}
}
bool LaunchClosure::builtAgainstDyldCache(uuid_t cacheUUID) const
{
uint32_t size;
const uint8_t* uuidBytes = (uint8_t*)findAttributePayload(Type::dyldCacheUUID, &size);
if ( uuidBytes == nullptr )
return false;
assert(size == sizeof(uuid_t));
memcpy(cacheUUID, uuidBytes, sizeof(uuid_t));
return true;
}
void LaunchClosure::forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const
{
forEachAttributePayload(Type::envVar, ^(const void* payload, uint32_t size, bool& stop) {
handler((char*)payload, stop);
});
}
ImageNum LaunchClosure::libSystemImageNum() const
{
uint32_t size;
const ImageNum* num = (ImageNum*)findAttributePayload(Type::libSystemNum, &size);
assert(num != nullptr);
assert(size == sizeof(ImageNum));
return *num;
}
void LaunchClosure::libDyldEntry(Image::ResolvedSymbolTarget& loc) const
{
uint32_t size;
const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::libDyldEntry, &size);
assert(data != nullptr);
assert(size == sizeof(Image::ResolvedSymbolTarget));
loc = *data;
}
bool LaunchClosure::mainEntry(Image::ResolvedSymbolTarget& mainLoc) const
{
uint32_t size;
const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::mainEntry, &size);
if ( data == nullptr )
return false;
assert(size == sizeof(Image::ResolvedSymbolTarget));
mainLoc = *data;
return true;
}
bool LaunchClosure::startEntry(Image::ResolvedSymbolTarget& startLoc) const
{
uint32_t size;
const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::startEntry, &size);
if ( data == nullptr )
return false;
assert(size == sizeof(Image::ResolvedSymbolTarget));
startLoc = *data;
return true;
}
const LaunchClosure::Flags& LaunchClosure::getFlags() const
{
uint32_t size;
const Flags* flags = (Flags*)findAttributePayload(Type::closureFlags, &size);
assert(flags != nullptr && "Closure missing Flags");
return *flags;
}
uint32_t LaunchClosure::initialLoadCount() const
{
return getFlags().initImageCount;
}
bool LaunchClosure::usedAtPaths() const
{
return getFlags().usedAtPaths;
}
bool LaunchClosure::usedFallbackPaths() const
{
return getFlags().usedFallbackPaths;
}
bool LaunchClosure::hasInsertedLibraries() const
{
return getFlags().hasInsertedLibraries;
}
bool LaunchClosure::hasProgramVars(uint32_t& runtimeOffset) const
{
if ( !getFlags().hasProgVars )
return false;
uint32_t payloadSize = 0;
const uint8_t* buffer = (const uint8_t*)findAttributePayload(Type::progVars, &payloadSize);
if (buffer == nullptr)
return false;
runtimeOffset = *((uint32_t*)buffer);
return true;
}
bool LaunchClosure::usedInterposing() const
{
return getFlags().usedInterposing;
}
bool LaunchClosure::hasInterposings() const
{
__block bool result = false;
forEachInterposingTuple(^(const InterposingTuple&, bool& stop) {
result = true;
stop = true;
});
return result;
}
void LaunchClosure::forEachInterposingTuple(void (^handler)(const InterposingTuple& tuple, bool& stop)) const
{
forEachAttributePayload(Type::interposeTuples, ^(const void* payload, uint32_t size, bool& stop) {
assert((size % sizeof(InterposingTuple)) == 0);
uintptr_t count = size / sizeof(InterposingTuple);
const InterposingTuple* tuples = (InterposingTuple*)payload;
for (uint32_t i=0; i < count && !stop; ++i) {
handler(tuples[i], stop);
}
});
}
bool LaunchClosure::selectorHashTable(Array<Image::ObjCSelectorImage>& imageNums,
const closure::ObjCSelectorOpt*& hashTable) const {
uint32_t payloadSize = 0;
const uint8_t* buffer = (const uint8_t*)findAttributePayload(Type::selectorTable, &payloadSize);
if (buffer == nullptr)
return false;
uint32_t count = 0;
memcpy(&count, buffer, sizeof(uint32_t));
buffer += sizeof(uint32_t);
imageNums = Array<Image::ObjCSelectorImage>((Image::ObjCSelectorImage*)buffer, count, count);
buffer += sizeof(Image::ObjCSelectorImage) * count;
hashTable = (const closure::ObjCSelectorOpt*)buffer;
return true;
}
bool LaunchClosure::classAndProtocolHashTables(Array<Image::ObjCClassImage>& imageNums,
const ObjCClassOpt*& classHashTable,
const ObjCClassOpt*& protocolHashTable) const {
uint32_t payloadSize = 0;
const uint8_t* buffer = (const uint8_t*)findAttributePayload(Type::classTable, &payloadSize);
if (buffer == nullptr)
return false;
uint32_t headerSize = sizeof(uint32_t) * 3;
uint32_t offsetToClassTable = 0;
uint32_t offsetToProtocolTable = 0;
uint32_t numImages = 0;
memcpy(&offsetToClassTable, buffer + 0, sizeof(uint32_t));
memcpy(&offsetToProtocolTable, buffer + 4, sizeof(uint32_t));
memcpy(&numImages, buffer + 8, sizeof(uint32_t));
imageNums = Array<Image::ObjCClassImage>((Image::ObjCClassImage*)(buffer + headerSize), numImages, numImages);
if ( offsetToClassTable != 0 )
classHashTable = (const ObjCClassOpt*)(buffer + offsetToClassTable);
if ( offsetToProtocolTable != 0 )
protocolHashTable = (const ObjCClassOpt*)(buffer + offsetToProtocolTable);
return true;
}
void LaunchClosure::duplicateClassesHashTable(const ObjCClassDuplicatesOpt*& duplicateClassesHashTable) const {
uint32_t payloadSize = 0;
const uint8_t* buffer = (const uint8_t*)findAttributePayload(Type::duplicateClassesTable, &payloadSize);
if (buffer == nullptr)
return;
duplicateClassesHashTable = (const ObjCClassDuplicatesOpt*)buffer;
}
static bool getContainerLibraryCachesDir(const char* envp[], char libCacheDir[])
{
const char* homeDir = _simple_getenv(envp, "HOME");
if ( homeDir == nullptr )
return false;
char realHomePath[PATH_MAX];
if ( realpath(homeDir, realHomePath) != nullptr )
homeDir = realHomePath;
#if TARGET_OS_OSX
if ( strstr(homeDir, "/Library/Containers/") == nullptr )
return false;
#else
if ( strncmp(homeDir, "/private/var/mobile/Containers/Data/", 36) != 0 )
return false;
#endif
strlcpy(libCacheDir, homeDir, PATH_MAX);
strlcat(libCacheDir, "/Library/Caches", PATH_MAX);
return true;
}
bool LaunchClosure::buildClosureCachePath(const char* mainExecutablePath, const char* envp[],
bool makeDirsIfMissing, char closurePath[])
{
if ( !getContainerLibraryCachesDir(envp, closurePath) )
return false;
struct stat statbuf;
if ( dyld3::stat(closurePath, &statbuf) != 0 )
return false;
strlcat(closurePath, "/com.apple.dyld", PATH_MAX);
if ( makeDirsIfMissing ) {
if ( dyld3::stat(closurePath, &statbuf) != 0 ) {
if ( ::mkdir(closurePath, S_IRWXU) != 0 )
return false;
}
}
const char* leafName = strrchr(mainExecutablePath, '/');
if ( leafName == nullptr )
leafName = mainExecutablePath;
else
++leafName;
strlcat(closurePath, "/", PATH_MAX);
strlcat(closurePath, leafName, PATH_MAX);
strlcat(closurePath, ".closure", PATH_MAX);
return true;
}
uint32_t ObjCStringTable::hash(const char *key, size_t keylen) const
{
uint64_t val = objc_opt::lookup8((uint8_t*)key, keylen, salt);
uint32_t index = (uint32_t)((shift == 64) ? 0 : (val>>shift)) ^ scramble[tab[val&mask]];
return index;
}
const char* ObjCStringTable::getString(const char* selName, const Array<uintptr_t>& baseAddresses) const {
StringTarget target = getPotentialTarget(selName);
if (target == sentinelTarget)
return nullptr;
dyld3::closure::Image::ObjCImageOffset imageAndOffset;
imageAndOffset.raw = target;
uintptr_t sectionBaseAddress = baseAddresses[imageAndOffset.imageIndex];
const char* value = (const char*)(sectionBaseAddress + imageAndOffset.imageOffset);
if (!strcmp(selName, value))
return value;
return nullptr;
}
bool ObjCSelectorOpt::getStringLocation(uint32_t index, const Array<closure::Image::ObjCSelectorImage>& selImages,
ImageNum& imageNum, uint64_t& vmOffset) const {
if ( index >= capacity )
return false;
StringTarget target = targets()[index];
if ( target == indexNotFound )
return false;
dyld3::closure::Image::ObjCImageOffset imageAndOffset;
imageAndOffset.raw = target;
imageNum = selImages[imageAndOffset.imageIndex].imageNum;
vmOffset = selImages[imageAndOffset.imageIndex].offset + imageAndOffset.imageOffset;
return true;;
}
void ObjCSelectorOpt::forEachString(const Array<Image::ObjCSelectorImage>& selectorImages,
void (^callback)(uint64_t selVMOffset, ImageNum imageNum)) const {
dyld3::Array<StringTarget> stringTargets = targets();
for (unsigned i = 0; i != capacity; ++i) {
dyld3::closure::Image::ObjCImageOffset imageAndOffset;
imageAndOffset.raw = stringTargets[i];
if (imageAndOffset.raw == sentinelTarget)
continue;
callback(selectorImages[imageAndOffset.imageIndex].offset + imageAndOffset.imageOffset,
selectorImages[imageAndOffset.imageIndex].imageNum);
}
}
void ObjCClassOpt::forEachClass(const char* className, const Array<std::pair<uintptr_t, uintptr_t>>& nameAndDataBaseAddresses,
void (^callback)(void* classPtr, bool isLoaded, bool* stop)) const {
uint32_t index = getIndex(className);
if ( index == closure::ObjCStringTable::indexNotFound )
return;
StringTarget target = targets()[index];
if ( target == sentinelTarget )
return;
closure::Image::ObjCImageOffset classNameImageAndOffset;
classNameImageAndOffset.raw = target;
uintptr_t nameBaseAddress = 0;
uintptr_t dataBaseAddress = 0;
std::tie(nameBaseAddress, dataBaseAddress) = nameAndDataBaseAddresses[classNameImageAndOffset.imageIndex];
const char* value = (const char*)(nameBaseAddress + classNameImageAndOffset.imageOffset);
if ( strcmp(className, value) != 0 )
return;
Array<closure::ObjCClassOpt::ClassTarget> classOffsetsArray = classOffsets();
Array<closure::ObjCClassOpt::ClassTarget> duplicatesArray = duplicateOffsets(duplicateCount());
const closure::ObjCClassOpt::ClassTarget& classOffset = classOffsetsArray[index];
if (classOffset.classData.isDuplicate == 0) {
void* classImpl = (void*)(dataBaseAddress + classOffset.classData.imageOffset);
bool stop = false;
callback(classImpl, true, &stop);
} else {
uint32_t duplicateCount = classOffset.duplicateData.count;
uint32_t duplicateStartIndex = classOffset.duplicateData.index;
for (uint32_t dupeIndex = 0; dupeIndex != duplicateCount; ++dupeIndex) {
closure::ObjCClassOpt::ClassTarget& duplicateClass = duplicatesArray[duplicateStartIndex + dupeIndex];
std::tie(nameBaseAddress, dataBaseAddress) = nameAndDataBaseAddresses[duplicateClass.classData.imageIndex];
void* classImpl = (void*)(dataBaseAddress + duplicateClass.classData.imageOffset);
bool stop = false;
callback(classImpl, true, &stop);
if (stop)
break;
}
}
}
void ObjCClassOpt::forEachClass(const Array<Image::ObjCClassImage>& classImages,
void (^nameCallback)(uint64_t classNameVMOffset, ImageNum imageNum),
void (^implCallback)(uint64_t classVMOffset, ImageNum imageNum)) const {
dyld3::Array<StringTarget> stringTargets = targets();
dyld3::Array<ObjCClassOpt::ClassTarget> classOffsetsArray = classOffsets();
dyld3::Array<ObjCClassOpt::ClassTarget> duplicatesArray = duplicateOffsets(duplicateCount());
for (unsigned i = 0; i != capacity; ++i) {
dyld3::closure::Image::ObjCImageOffset classNameImageAndOffset;
classNameImageAndOffset.raw = stringTargets[i];
if (classNameImageAndOffset.raw == sentinelTarget)
continue;
nameCallback(classImages[classNameImageAndOffset.imageIndex].offsetOfClassNames + classNameImageAndOffset.imageOffset,
classImages[classNameImageAndOffset.imageIndex].imageNum);
const ObjCClassOpt::ClassTarget& classOffset = classOffsetsArray[i];
if (classOffset.classData.isDuplicate == 0) {
implCallback(classImages[classOffset.classData.imageIndex].offsetOfClasses + classOffset.classData.imageOffset,
classImages[classOffset.classData.imageIndex].imageNum);
} else {
uint32_t duplicateCount = classOffset.duplicateData.count;
uint32_t duplicateStartIndex = classOffset.duplicateData.index;
for (uint32_t dupeIndex = 0; dupeIndex != duplicateCount; ++dupeIndex) {
ObjCClassOpt::ClassTarget& duplicateClass = duplicatesArray[duplicateStartIndex + dupeIndex];
implCallback(classImages[duplicateClass.classData.imageIndex].offsetOfClasses + duplicateClass.classData.imageOffset,
classImages[duplicateClass.classData.imageIndex].imageNum);
}
}
}
}
bool ObjCClassDuplicatesOpt::getClassLocation(const char* className, const objc_opt::objc_opt_t* objCOpt, void*& classImpl) const {
uint32_t potentialTarget = getPotentialTarget(className);
if (potentialTarget == sentinelTarget)
return false;
objc_opt::objc_clsopt_t* clsOpt = objCOpt->clsopt();
Image::ObjCDuplicateClass duplicateClass;
duplicateClass.raw = potentialTarget;
const char* sharedCacheClassName = clsOpt->getClassNameForIndex(duplicateClass.sharedCacheClassOptIndex);
if (strcmp(className, sharedCacheClassName) != 0)
return false;
classImpl = clsOpt->getClassForIndex(duplicateClass.sharedCacheClassOptIndex, duplicateClass.sharedCacheClassDuplicateIndex);
return true;
}
void ObjCClassDuplicatesOpt::forEachClass(void (^callback)(Image::ObjCDuplicateClass duplicateClass)) const {
dyld3::Array<StringTarget> stringTargets = targets();
for (unsigned i = 0; i != capacity; ++i) {
StringTarget target = stringTargets[i];
if ( target == sentinelTarget )
continue;
Image::ObjCDuplicateClass duplicateClass;
duplicateClass.raw = (uint32_t)target;
callback(duplicateClass);
}
}
} }