ClosureBuilder.cpp [plain text]
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/mman.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include "mach-o/dyld_priv.h"
#include "ClosureWriter.h"
#include "ClosureBuilder.h"
#include "MachOAnalyzer.h"
#include "libdyldEntryVector.h"
#include "Tracing.h"
namespace dyld3 {
namespace closure {
const DlopenClosure* ClosureBuilder::sRetryDlopenClosure = (const DlopenClosure*)(-1);
ClosureBuilder::ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
const PathOverrides& pathOverrides, AtPath atPathHandling, LaunchErrorInfo* errorInfo,
const char* archName, Platform platform,
const CacheDylibsBindingHandlers* handlers)
: _fileSystem(fileSystem), _dyldCache(dyldCache), _pathOverrides(pathOverrides), _archName(archName), _platform(platform), _startImageNum(startImageNum),
_handlers(handlers), _atPathHandling(atPathHandling), _launchErrorInfo(errorInfo), _dyldCacheIsLive(dyldCacheIsLive)
{
if ( dyldCache != nullptr ) {
_dyldImageArray = dyldCache->cachedDylibsImageArray();
if ( (dyldCache->header.otherImageArrayAddr != 0) && (dyldCache->header.progClosuresSize == 0) )
_makingClosuresInCache = true;
}
}
ClosureBuilder::~ClosureBuilder() {
if ( _tempPaths != nullptr )
PathPool::deallocate(_tempPaths);
if ( _mustBeMissingPaths != nullptr )
PathPool::deallocate(_mustBeMissingPaths);
}
bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, bool staticLinkage, bool allowOther)
{
__block bool result = false;
_pathOverrides.forEachPathVariant(loadPath, ^(const char* possiblePath, bool isFallbackPath, bool& stop) {
bool unmapWhenDone = false;
bool contentRebased = false;
bool hasInits = false;
bool fileFound = false;
bool markNeverUnload = staticLinkage ? forImageChain.image.markNeverUnload : false;
ImageNum overrideImageNum = 0;
ImageNum foundImageNum = 0;
const MachOAnalyzer* mh = nullptr;
const char* filePath = nullptr;
LoadedFileInfo loadedFileInfo;
bool isRPath = (strncmp(possiblePath, "@rpath/", 7) == 0);
bool implictRPath = !staticLinkage && (loadPath[0] != '/') && (loadPath == possiblePath) && (_atPathHandling != AtPath::none);
const char* prePathVarExpansion = possiblePath;
possiblePath = resolvePathVar(possiblePath, forImageChain, implictRPath);
if ( prePathVarExpansion != possiblePath )
_atPathUsed = true;
const char* leafName = strrchr(possiblePath, '/');
for (BuilderLoadedImage& li: _loadedImages) {
if ( strcmp(li.path(), possiblePath) == 0 ) {
foundImage = &li;
result = true;
stop = true;
return;
}
else if ( isRPath ) {
if (const char* aLeaf = strrchr(li.path(), '/')) {
if ( strcmp(aLeaf, leafName) == 0 ) {
if ( li.loadAddress()->isDylib() && (strcmp(loadPath, li.loadAddress()->installName()) == 0) ) {
foundImage = &li;
result = true;
stop = true;
return;
}
}
}
}
}
if ( _fileSystem.fileExists(possiblePath, &loadedFileInfo.inode, &loadedFileInfo.mtime) ) {
fileFound = true;
for (BuilderLoadedImage& li: _loadedImages) {
if ( (li.loadedFileInfo.inode == loadedFileInfo.inode) && (li.loadedFileInfo.mtime == loadedFileInfo.mtime) ) {
foundImage = &li;
result = true;
stop = true;
return;
}
}
}
filePath = possiblePath;
char realPath[MAXPATHLEN];
if ( _dyldImageArray != nullptr && (_dyldCache->header.formatVersion == dyld3::closure::kFormatVersion) ) {
uint32_t dyldCacheImageIndex;
bool foundInCache = _dyldCache->hasImagePath(possiblePath, dyldCacheImageIndex);
if ( !foundInCache && fileFound ) {
if ( allowOther ) {
if (const dyld3::closure::Image* otherImage = _dyldCache->findDlopenOtherImage(possiblePath) ) {
uint64_t expectedInode;
uint64_t expectedModTime;
if ( !otherImage->isInvalid() ) {
bool hasInodeInfo = otherImage->hasFileModTimeAndInode(expectedInode, expectedModTime);
if ( !hasInodeInfo || ((expectedInode == loadedFileInfo.inode) && (expectedModTime == loadedFileInfo.mtime)) ) {
loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, possiblePath, _archName, _platform);
if ( _diag.noError() ) {
mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
foundImageNum = otherImage->imageNum();
unmapWhenDone = true;
contentRebased = false;
hasInits = otherImage->hasInitializers() || otherImage->mayHavePlusLoads();
}
}
}
}
}
if ( mh == nullptr ) {
if ( _fileSystem.getRealPath(possiblePath, realPath) ) {
foundInCache = _dyldCache->hasImagePath(realPath, dyldCacheImageIndex);
if ( foundInCache ) {
filePath = realPath;
#if BUILDING_LIBDYLD
if ( foundInCache ) {
for (BuilderLoadedImage& li: _loadedImages) {
if ( strcmp(li.path(), realPath) == 0 ) {
foundImage = &li;
result = true;
stop = true;
return;
}
}
}
#endif
}
}
}
}
if ( foundInCache ) {
ImageNum dyldCacheImageNum = dyldCacheImageIndex + 1;
bool useCache = true;
markNeverUnload = true; const Image* image = _dyldImageArray->imageForNum(dyldCacheImageNum);
if ( image->overridableDylib() ) {
if ( fileFound && (_platform == MachOFile::currentPlatform()) ) {
uint64_t expectedInode;
uint64_t expectedModTime;
if ( image->hasFileModTimeAndInode(expectedInode, expectedModTime) ) {
useCache = ( (loadedFileInfo.inode == expectedInode) && (loadedFileInfo.mtime == expectedModTime) );
}
else if ( _makingClosuresInCache ) {
useCache = true;
}
else {
useCache = false;
}
}
if ( !useCache )
overrideImageNum = dyldCacheImageNum;
}
if ( useCache ) {
foundImageNum = dyldCacheImageNum;
mh = (MachOAnalyzer*)_dyldCache->getIndexedImageEntry(foundImageNum-1, loadedFileInfo.mtime, loadedFileInfo.inode);
unmapWhenDone = false;
contentRebased = !_makingDyldCacheImages && _dyldCacheIsLive;
hasInits = image->hasInitializers() || image->mayHavePlusLoads();
}
}
}
if (_makingDyldCacheImages) {
addMustBeMissingPath(possiblePath);
return;
}
if ( mh == nullptr ) {
loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, filePath, _archName, _platform);
mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
if ( mh == nullptr ) {
if (_isLaunchClosure) {
addMustBeMissingPath(possiblePath);
}
return;
}
if ( staticLinkage ) {
if ( !mh->isDylib() ) {
_diag.error("not a dylib");
return;
}
}
else if ( mh->isMainExecutable() ) {
if ( !mh->isPIE() || !mh->isDynamicExecutable() ) {
_diag.error("not PIE");
return;
}
}
foundImageNum = _startImageNum + _nextIndex++;
unmapWhenDone = true;
} else {
loadedFileInfo.fileContent = mh;
}
if ( filePath != loadPath ) {
filePath = strdup_temp(filePath);
if ( overrideImageNum == 0 ) {
if ( _dyldImageArray != nullptr ) {
uint32_t dyldCacheImageIndex;
if ( _dyldCache->hasImagePath(loadPath, dyldCacheImageIndex) ) {
ImageNum possibleOverrideNum = dyldCacheImageIndex+1;
if ( possibleOverrideNum != foundImageNum )
overrideImageNum = possibleOverrideNum;
}
}
}
}
if ( !markNeverUnload ) {
if ( mh->hasThreadLocalVariables() ) {
markNeverUnload = true;
} else if ( mh->hasObjC() && mh->isDylib() ) {
markNeverUnload = true;
} else {
__block bool hasDOFs = false;
mh->forEachDOFSection(_diag, ^(uint32_t offset) {
hasDOFs = true;
});
if ( hasDOFs )
markNeverUnload = true;
}
}
loadedFileInfo.path = filePath;
BuilderLoadedImage entry;
entry.loadedFileInfo = loadedFileInfo;
entry.imageNum = foundImageNum;
entry.unmapWhenDone = unmapWhenDone;
entry.contentRebased = contentRebased;
entry.hasInits = hasInits;
entry.markNeverUnload = markNeverUnload;
entry.rtldLocal = false;
entry.isBadImage = false;
entry.overrideImageNum = overrideImageNum;
_loadedImages.push_back(entry);
foundImage = &_loadedImages.back();
if ( isFallbackPath )
_fallbackPathUsed = true;
stop = true;
result = true;
}, _platform);
return result;
}
bool ClosureBuilder::expandAtLoaderPath(const char* loadPath, bool fromLCRPATH, const BuilderLoadedImage& loadedImage, char fixedPath[])
{
switch ( _atPathHandling ) {
case AtPath::none:
return false;
case AtPath::onlyInRPaths:
if ( !fromLCRPATH ) {
if ( _isLaunchClosure )
return false;
}
break;
case AtPath::all:
break;
}
if ( strncmp(loadPath, "@loader_path/", 13) != 0 )
return false;
strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
char* lastSlash = strrchr(fixedPath, '/');
if ( lastSlash != nullptr ) {
strcpy(lastSlash+1, &loadPath[13]);
return true;
}
return false;
}
bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, char fixedPath[])
{
switch ( _atPathHandling ) {
case AtPath::none:
return false;
case AtPath::onlyInRPaths:
if ( !fromLCRPATH )
return false;
break;
case AtPath::all:
break;
}
if ( strncmp(loadPath, "@executable_path/", 17) != 0 )
return false;
if ( _atPathHandling != AtPath::all )
return false;
strlcpy(fixedPath, _loadedImages[_mainProgLoadIndex].path(), PATH_MAX);
char* lastSlash = strrchr(fixedPath, '/');
if ( lastSlash != nullptr ) {
strcpy(lastSlash+1, &loadPath[17]);
return true;
}
return false;
}
const char* ClosureBuilder::resolvePathVar(const char* loadPath, const LoadedImageChain& forImageChain, bool implictRPath)
{
if ( (_atPathHandling == AtPath::none) && (loadPath[0] == '@') )
return loadPath;
if ( !implictRPath && (loadPath[0] != '@') )
return loadPath;
BLOCK_ACCCESSIBLE_ARRAY(char, tempPath, PATH_MAX); if ( expandAtLoaderPath(loadPath, false, forImageChain.image, tempPath) )
return strdup_temp(tempPath);
if ( expandAtExecutablePath(loadPath, false, tempPath) )
return strdup_temp(tempPath);
const char* rpathTail = nullptr;
char implicitRpathBuffer[PATH_MAX];
if ( strncmp(loadPath, "@rpath/", 7) == 0 ) {
rpathTail = &loadPath[6];
}
else if ( implictRPath ) {
strlcpy(implicitRpathBuffer, "/", PATH_MAX);
strlcat(implicitRpathBuffer, loadPath, PATH_MAX);
rpathTail = implicitRpathBuffer;
}
if ( rpathTail != nullptr ) {
__block const char* result = nullptr;
for (const LoadedImageChain* link = &forImageChain; (link != nullptr) && (result == nullptr); link = link->previous) {
link->image.loadAddress()->forEachRPath(^(const char* rPath, bool& stop) {
if ( expandAtLoaderPath(rPath, true, link->image, tempPath) || expandAtExecutablePath(rPath, true, tempPath) ) {
strlcat(tempPath, rpathTail, PATH_MAX);
}
else {
strlcpy(tempPath, rPath, PATH_MAX);
strlcat(tempPath, rpathTail, PATH_MAX);
}
if ( _fileSystem.fileExists(tempPath) ) {
stop = true;
result = strdup_temp(tempPath);
}
else {
if (_isLaunchClosure) {
addMustBeMissingPath(tempPath);
}
}
});
}
if ( result != nullptr )
return result;
}
return loadPath;
}
const char* ClosureBuilder::strdup_temp(const char* path)
{
if ( _tempPaths == nullptr )
_tempPaths = PathPool::allocate();
return _tempPaths->add(path);
}
void ClosureBuilder::addMustBeMissingPath(const char* path)
{
if ( _mustBeMissingPaths == nullptr )
_mustBeMissingPaths = PathPool::allocate();
_mustBeMissingPaths->add(path);
}
ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(ImageNum imageNum)
{
for (BuilderLoadedImage& li : _loadedImages) {
if ( li.imageNum == imageNum ) {
return li;
}
}
for (BuilderLoadedImage& li : _loadedImages) {
if ( li.overrideImageNum == imageNum ) {
return li;
}
}
assert(0 && "LoadedImage not found");
}
ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(const MachOAnalyzer* mh)
{
for (BuilderLoadedImage& li : _loadedImages) {
if ( li.loadAddress() == mh ) {
return li;
}
}
assert(0 && "LoadedImage not found");
}
const MachOAnalyzer* ClosureBuilder::machOForImageNum(ImageNum imageNum)
{
return findLoadedImage(imageNum).loadAddress();
}
const MachOAnalyzer* ClosureBuilder::findDependent(const MachOLoaded* mh, uint32_t depIndex)
{
for (const BuilderLoadedImage& li : _loadedImages) {
if ( li.loadAddress() == mh ) {
if (li.isBadImage) {
return nullptr;
}
ImageNum childNum = li.dependents[depIndex].imageNum();
return machOForImageNum(childNum);
}
}
return nullptr;
}
ImageNum ClosureBuilder::imageNumForMachO(const MachOAnalyzer* mh)
{
for (const BuilderLoadedImage& li : _loadedImages) {
if ( li.loadAddress() == mh ) {
return li.imageNum;
}
}
assert(0 && "unknown mach-o");
return 0;
}
void ClosureBuilder::recursiveLoadDependents(LoadedImageChain& forImageChain)
{
if ( forImageChain.image.dependents.begin() != nullptr )
return;
uintptr_t startDepIndex = _dependencies.count();
__block uint32_t depIndex = 0;
forImageChain.image.loadAddress()->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
Image::LinkKind kind = Image::LinkKind::regular;
if ( isWeak )
kind = Image::LinkKind::weak;
else if ( isReExport )
kind = Image::LinkKind::reExport;
else if ( isUpward )
kind = Image::LinkKind::upward;
BuilderLoadedImage* foundImage;
if ( findImage(loadPath, forImageChain, foundImage, true, false) ) {
if ( foundImage->loadAddress()->filetype != MH_DYLIB ) {
_diag.error("found '%s' which is not a dylib. Needed by '%s'", foundImage->path(), forImageChain.image.path());
}
else {
const char* installName;
uint32_t foundCompatVers;
uint32_t foundCurrentVers;
foundImage->loadAddress()->getDylibInstallName(&installName, &foundCompatVers, &foundCurrentVers);
if ( (foundCompatVers < compatVersion) && foundImage->loadAddress()->enforceCompatVersion() ) {
char foundStr[32];
char requiredStr[32];
MachOFile::packedVersionToString(foundCompatVers, foundStr);
MachOFile::packedVersionToString(compatVersion, requiredStr);
_diag.error("found '%s' which has compat version (%s) which is less than required (%s). Needed by '%s'",
foundImage->path(), foundStr, requiredStr, forImageChain.image.path());
}
}
if ( _diag.noError() )
_dependencies.push_back(Image::LinkedImage(kind, foundImage->imageNum));
}
else if ( isWeak ) {
_dependencies.push_back(Image::LinkedImage(Image::LinkKind::weak, kMissingWeakLinkedImage));
}
else {
BLOCK_ACCCESSIBLE_ARRAY(char, extra, 4096);
extra[0] = '\0';
const char* targetLeaf = strrchr(loadPath, '/');
if ( targetLeaf == nullptr )
targetLeaf = loadPath;
if ( _mustBeMissingPaths != nullptr ) {
strcpy(extra, ", tried: ");
_mustBeMissingPaths->forEachPath(^(const char* aPath) {
const char* aLeaf = strrchr(aPath, '/');
if ( aLeaf == nullptr )
aLeaf = aPath;
if ( strcmp(targetLeaf, aLeaf) == 0 ) {
strlcat(extra, "'", 4096);
strlcat(extra, aPath, 4096);
strlcat(extra, "' ", 4096);
}
});
}
if ( _diag.hasError() ) {
#if BUILDING_CACHE_BUILDER
std::string errorMessageBuffer = _diag.errorMessage();
const char* msg = errorMessageBuffer.c_str();
#else
const char* msg = _diag.errorMessage();
#endif
char msgCopy[strlen(msg)+4];
strcpy(msgCopy, msg);
_diag.error("dependent dylib '%s' not found for '%s'. %s", loadPath, forImageChain.image.path(), msgCopy);
}
else {
_diag.error("dependent dylib '%s' not found for '%s'%s", loadPath, forImageChain.image.path(), extra);
}
if ( _launchErrorInfo != nullptr ) {
_launchErrorInfo->kind = DYLD_EXIT_REASON_DYLIB_MISSING;
_launchErrorInfo->clientOfDylibPath = forImageChain.image.path();
_launchErrorInfo->targetDylibPath = loadPath;
_launchErrorInfo->symbol = nullptr;
}
}
++depIndex;
if ( _diag.hasError() )
stop = true;
});
if ( _diag.hasError() )
return;
forImageChain.image.dependents = _dependencies.subArray(startDepIndex, depIndex);
for (Image::LinkedImage dep : forImageChain.image.dependents) {
if ( dep.kind() == Image::LinkKind::upward )
continue;
if ( (dep.kind() == Image::LinkKind::weak) && (dep.imageNum() == kMissingWeakLinkedImage) )
continue;
BuilderLoadedImage& depLoadedImage = findLoadedImage(dep.imageNum());
LoadedImageChain chain = { &forImageChain, depLoadedImage };
recursiveLoadDependents(chain);
if ( _diag.hasError() )
break;
}
}
void ClosureBuilder::loadDanglingUpwardLinks()
{
bool danglingFixed;
do {
danglingFixed = false;
for (BuilderLoadedImage& li : _loadedImages) {
if ( li.dependents.begin() == nullptr ) {
LoadedImageChain chain = { nullptr, li };
recursiveLoadDependents(chain);
danglingFixed = true;
break;
}
}
} while (danglingFixed && _diag.noError());
}
bool ClosureBuilder::overridableDylib(const BuilderLoadedImage& forImage)
{
if ( !_makingDyldCacheImages )
return false;
if ( _platform == Platform::macOS )
return true;
if ( !_makingCustomerCache )
return true;
return false; }
void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImage)
{
const MachOAnalyzer* macho = forImage.loadAddress();
writer.setImageNum(forImage.imageNum);
writer.setHasWeakDefs(macho->hasWeakDefs());
writer.setIsBundle(macho->isBundle());
writer.setIsDylib(macho->isDylib());
writer.setIs64(macho->is64());
writer.setIsExecutable(macho->isMainExecutable());
writer.setUses16KPages(macho->uses16KPages());
writer.setOverridableDylib(overridableDylib(forImage));
writer.setInDyldCache(macho->inDyldCache());
if ( macho->hasObjC() ) {
writer.setHasObjC(true);
bool hasPlusLoads = macho->hasPlusLoadMethod(_diag);
writer.setHasPlusLoads(hasPlusLoads);
if ( hasPlusLoads )
forImage.hasInits = true;
}
else {
writer.setHasObjC(false);
writer.setHasPlusLoads(false);
}
if ( forImage.markNeverUnload ) {
writer.setNeverUnload(true);
}
#if BUILDING_DYLD || BUILDING_LIBDYLD
writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime);
#else
if ( _platform == Platform::macOS ) {
if ( macho->inDyldCache() && !_dyldCache->header.dylibsExpectedOnDisk ) {
}
else {
writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime);
}
}
else {
}
#endif
if ( !macho->inDyldCache() ) {
writer.setMappingInfo(forImage.loadedFileInfo.sliceOffset, macho->mappedSize());
uint32_t codeSigFileOffset;
uint32_t codeSigSize;
if ( macho->hasCodeSignature(codeSigFileOffset, codeSigSize) ) {
writer.setCodeSignatureLocation(codeSigFileOffset, codeSigSize);
uint8_t cdHash[20];
if ( macho->getCDHash(cdHash) )
writer.setCDHash(cdHash);
}
uint32_t fairPlayFileOffset;
uint32_t fairPlaySize;
if ( macho->isFairPlayEncrypted(fairPlayFileOffset, fairPlaySize) ) {
writer.setFairPlayEncryptionRange(fairPlayFileOffset, fairPlaySize);
}
}
writer.addPath(forImage.path());
if ( _aliases != nullptr ) {
for (const CachedDylibAlias& alias : *_aliases) {
if ( strcmp(alias.realPath, forImage.path()) == 0 )
writer.addPath(alias.aliasPath);
}
}
uuid_t uuid;
if ( macho->getUuid(uuid) )
writer.setUUID(uuid);
writer.setDependents(forImage.dependents);
addSegments(writer, macho);
if ( forImage.overrideImageNum != 0 ) {
writer.setAsOverrideOf(forImage.overrideImageNum);
const char* overridePath = _dyldImageArray->imageForNum(forImage.overrideImageNum)->path();
writer.addPath(overridePath);
if ( strcmp(overridePath, "/usr/lib/system/libdyld.dylib") == 0 )
_libDyldImageNum = forImage.imageNum;
else if ( strcmp(overridePath, "/usr/lib/libSystem.B.dylib") == 0 )
_libSystemImageNum = forImage.imageNum;
}
if ( !macho->inDyldCache() || _makingDyldCacheImages ) {
if ( macho->hasChainedFixups() ) {
addChainedFixupInfo(writer, forImage);
}
else {
if ( _handlers != nullptr ) {
reportRebasesAndBinds(writer, forImage);
}
else {
addRebaseInfo(writer, macho);
if ( _diag.noError() )
addBindInfo(writer, forImage);
}
}
}
if ( _diag.hasError() ) {
writer.setInvalid();
return;
}
bool contentRebased = forImage.contentRebased;
__block unsigned initCount = 0;
macho->forEachInitializer(_diag, contentRebased, ^(uint32_t offset) {
++initCount;
}, _dyldCache);
if ( initCount != 0 ) {
BLOCK_ACCCESSIBLE_ARRAY(uint32_t, initOffsets, initCount);
__block unsigned index = 0;
macho->forEachInitializer(_diag, contentRebased, ^(uint32_t offset) {
initOffsets[index++] = offset;
}, _dyldCache);
writer.setInitOffsets(initOffsets, initCount);
forImage.hasInits = true;
}
STACK_ALLOC_ARRAY(uint32_t, dofSectionOffsets, 256);
macho->forEachDOFSection(_diag, ^(uint32_t offset) {
dofSectionOffsets.push_back(offset);
});
if ( !dofSectionOffsets.empty() ) {
writer.setDofOffsets(dofSectionOffsets);
}
}
void ClosureBuilder::addSegments(ImageWriter& writer, const MachOAnalyzer* mh)
{
const uint32_t segCount = mh->segmentCount();
if ( mh->inDyldCache() ) {
uint64_t cacheUnslideBaseAddress = _dyldCache->unslidLoadAddress();
BLOCK_ACCCESSIBLE_ARRAY(Image::DyldCacheSegment, segs, segCount);
mh->forEachSegment(^(const MachOAnalyzer::SegmentInfo& info, bool& stop) {
segs[info.segIndex] = { (uint32_t)(info.vmAddr-cacheUnslideBaseAddress), (uint32_t)info.vmSize, info.protections };
});
writer.setCachedSegments(segs, segCount);
}
else {
const uint32_t pageSize = (mh->uses16KPages() ? 0x4000 : 0x1000);
__block uint32_t diskSegIndex = 0;
__block uint32_t totalPageCount = 0;
__block uint32_t lastFileOffsetEnd = 0;
__block uint64_t lastVmAddrEnd = 0;
BLOCK_ACCCESSIBLE_ARRAY(Image::DiskSegment, dsegs, segCount*3); mh->forEachSegment(^(const MachOAnalyzer::SegmentInfo& info, bool& stop) {
if ( (info.fileOffset != 0) && (info.fileOffset != lastFileOffsetEnd) ) {
Image::DiskSegment filePadding;
filePadding.filePageCount = (info.fileOffset - lastFileOffsetEnd)/pageSize;
filePadding.vmPageCount = 0;
filePadding.permissions = 0;
filePadding.paddingNotSeg = 1;
dsegs[diskSegIndex++] = filePadding;
}
if ( (lastVmAddrEnd != 0) && (info.vmAddr != lastVmAddrEnd) ) {
Image::DiskSegment vmPadding;
vmPadding.filePageCount = 0;
vmPadding.vmPageCount = (info.vmAddr - lastVmAddrEnd)/pageSize;
vmPadding.permissions = 0;
vmPadding.paddingNotSeg = 1;
dsegs[diskSegIndex++] = vmPadding;
totalPageCount += vmPadding.vmPageCount;
}
{
Image::DiskSegment segInfo;
segInfo.filePageCount = (info.fileSize+pageSize-1)/pageSize;
segInfo.vmPageCount = (info.vmSize+pageSize-1)/pageSize;
segInfo.permissions = info.protections & 7;
segInfo.paddingNotSeg = 0;
dsegs[diskSegIndex++] = segInfo;
totalPageCount += segInfo.vmPageCount;
if ( info.fileSize != 0 )
lastFileOffsetEnd = (uint32_t)(info.fileOffset + info.fileSize);
if ( info.vmSize != 0 )
lastVmAddrEnd = info.vmAddr + info.vmSize;
}
});
writer.setDiskSegments(dsegs, diskSegIndex);
}
}
void ClosureBuilder::addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh)
{
const unsigned pointerSize = mh->pointerSize();
mh->forEachInterposingSection(_diag, ^(uint64_t sectVmOffset, uint64_t sectVmSize, bool &stop) {
const uint32_t entrySize = 2*pointerSize;
const uint32_t tupleCount = (uint32_t)(sectVmSize/entrySize);
BLOCK_ACCCESSIBLE_ARRAY(InterposingTuple, resolvedTuples, tupleCount);
for (uint32_t i=0; i < tupleCount; ++i) {
resolvedTuples[i].stockImplementation.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
resolvedTuples[i].stockImplementation.absolute.value = 0;
resolvedTuples[i].newImplementation.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
resolvedTuples[i].newImplementation.absolute.value = 0;
}
image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &rebaseStop) {
if ( imageOffsetToRebase < sectVmOffset )
return;
if ( imageOffsetToRebase > sectVmOffset+sectVmSize )
return;
uint64_t offsetIntoSection = imageOffsetToRebase - sectVmOffset;
uint64_t rebaseIndex = offsetIntoSection/entrySize;
if ( rebaseIndex*entrySize != offsetIntoSection )
return;
const void* content = (uint8_t*)mh + imageOffsetToRebase;
uint64_t unslidTargetAddress = mh->is64() ? *(uint64_t*)content : *(uint32_t*)content;
resolvedTuples[rebaseIndex].newImplementation.image.kind = Image::ResolvedSymbolTarget::kindImage;
resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
resolvedTuples[rebaseIndex].newImplementation.image.offset = unslidTargetAddress - mh->preferredLoadAddress();
}, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget bindTarget, bool &bindStop) {
if ( imageOffsetToBind < sectVmOffset )
return;
if ( imageOffsetToBind > sectVmOffset+sectVmSize )
return;
uint64_t offsetIntoSection = imageOffsetToBind - sectVmOffset;
uint64_t bindIndex = offsetIntoSection/entrySize;
if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
return;
resolvedTuples[bindIndex].stockImplementation = bindTarget;
}, ^(uint64_t imageOffsetStart, const Array<Image::ResolvedSymbolTarget>& targets, bool& chainStop) {
image->forEachChainedFixup((void*)mh, imageOffsetStart, ^(uint64_t* fixupLoc, MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stopChain) {
uint64_t imageOffsetToFixup = (uint64_t)fixupLoc - (uint64_t)mh;
if ( fixupInfo.authRebase.auth ) {
#if SUPPORT_ARCH_arm64e
if ( fixupInfo.authBind.bind ) {
closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.authBind.ordinal];
if ( imageOffsetToFixup < sectVmOffset )
return;
if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
return;
uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
uint64_t bindIndex = offsetIntoSection/entrySize;
if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
return;
resolvedTuples[bindIndex].stockImplementation = bindTarget;
}
else {
if ( imageOffsetToFixup < sectVmOffset )
return;
if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
return;
uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
uint64_t rebaseIndex = offsetIntoSection/entrySize;
if ( rebaseIndex*entrySize != offsetIntoSection )
return;
uint64_t unslidTargetAddress = (uint64_t)mh->preferredLoadAddress() + fixupInfo.authRebase.target;
resolvedTuples[rebaseIndex].newImplementation.image.kind = Image::ResolvedSymbolTarget::kindImage;
resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
resolvedTuples[rebaseIndex].newImplementation.image.offset = unslidTargetAddress - mh->preferredLoadAddress();
}
#else
_diag.error("malformed chained pointer");
stop = true;
stopChain = true;
#endif
}
else {
if ( fixupInfo.plainRebase.bind ) {
closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.plainBind.ordinal];
if ( imageOffsetToFixup < sectVmOffset )
return;
if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
return;
uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
uint64_t bindIndex = offsetIntoSection/entrySize;
if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
return;
resolvedTuples[bindIndex].stockImplementation = bindTarget;
}
else {
if ( imageOffsetToFixup < sectVmOffset )
return;
if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
return;
uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
uint64_t rebaseIndex = offsetIntoSection/entrySize;
if ( rebaseIndex*entrySize != offsetIntoSection )
return;
uint64_t unslidTargetAddress = fixupInfo.plainRebase.signExtendedTarget();
resolvedTuples[rebaseIndex].newImplementation.image.kind = Image::ResolvedSymbolTarget::kindImage;
resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
resolvedTuples[rebaseIndex].newImplementation.image.offset = unslidTargetAddress - mh->preferredLoadAddress();
}
}
});
});
STACK_ALLOC_ARRAY(InterposingTuple, goodTuples, tupleCount);
for (uint32_t i=0; i < tupleCount; ++i) {
if ( (resolvedTuples[i].stockImplementation.image.kind != Image::ResolvedSymbolTarget::kindAbsolute)
&& (resolvedTuples[i].newImplementation.image.kind != Image::ResolvedSymbolTarget::kindAbsolute) )
goodTuples.push_back(resolvedTuples[i]);
}
writer.addInterposingTuples(goodTuples);
STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, goodTuples.count());
for (const InterposingTuple& aTuple : goodTuples) {
if ( aTuple.stockImplementation.sharedCache.kind == Image::ResolvedSymbolTarget::kindSharedCache ) {
uint32_t imageIndex;
assert(_dyldCache->addressInText((uint32_t)aTuple.stockImplementation.sharedCache.offset, &imageIndex));
ImageNum imageInCache = imageIndex+1;
Closure::PatchEntry patch;
patch.exportCacheOffset = (uint32_t)aTuple.stockImplementation.sharedCache.offset;
patch.overriddenDylibInCache = imageInCache;
patch.replacement = aTuple.newImplementation;
patches.push_back(patch);
}
}
writer.addCachePatches(patches);
});
}
void ClosureBuilder::addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh)
{
const uint64_t ptrSize = mh->pointerSize();
Image::RebasePattern maxLeapPattern = { 0xFFFFF, 0, 0xF };
const uint64_t maxLeapCount = maxLeapPattern.repeatCount * maxLeapPattern.skipCount;
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::RebasePattern, rebaseEntries, 1024);
__block uint64_t lastLocation = -ptrSize;
mh->forEachRebase(_diag, true, ^(uint64_t runtimeOffset, bool& stop) {
const uint64_t delta = runtimeOffset - lastLocation;
const bool aligned = ((delta % ptrSize) == 0);
if ( delta == ptrSize ) {
if ( rebaseEntries.back().contigCount < 255 ) {
rebaseEntries.back().contigCount++;
}
else {
rebaseEntries.push_back({ 1, 1, 0 });
}
}
else if ( aligned && (delta <= (ptrSize*15)) ) {
rebaseEntries.back().skipCount = (uint8_t)((delta-ptrSize)/ptrSize);
int lastIndex = (int)(rebaseEntries.count() - 1);
if ( lastIndex > 1 ) {
if ( (rebaseEntries[lastIndex].contigCount == rebaseEntries[lastIndex-1].contigCount)
&& (rebaseEntries[lastIndex].skipCount == rebaseEntries[lastIndex-1].skipCount) ) {
rebaseEntries.pop_back();
rebaseEntries.back().repeatCount += 1;
}
}
rebaseEntries.push_back({ 1, 1, 0 });
}
else {
uint64_t advanceCount = (delta-ptrSize);
if ( (runtimeOffset < lastLocation) && (lastLocation != -ptrSize) ) {
rebaseEntries.push_back({ 0, 0, 0 });
advanceCount = runtimeOffset;
}
while ( advanceCount > maxLeapCount ) {
rebaseEntries.push_back(maxLeapPattern);
advanceCount -= maxLeapCount;
}
while ( advanceCount > maxLeapPattern.repeatCount ) {
uint64_t count = advanceCount / maxLeapPattern.skipCount;
rebaseEntries.push_back({ (uint32_t)count, 0, maxLeapPattern.skipCount });
advanceCount -= (count*maxLeapPattern.skipCount);
}
if ( advanceCount != 0 )
rebaseEntries.push_back({ (uint32_t)advanceCount, 0, 1 });
rebaseEntries.push_back({ 1, 1, 0 });
}
lastLocation = runtimeOffset;
});
writer.setRebaseInfo(rebaseEntries);
if ( mh->cputype == CPU_TYPE_I386 ) {
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::TextFixupPattern, textRebases, 512);
__block uint64_t lastOffset = -4;
mh->forEachTextRebase(_diag, ^(uint64_t runtimeOffset, bool& stop) {
if ( textRebases.freeCount() < 2 ) {
_diag.error("too many text rebase locations (%ld) in %s", textRebases.maxCount(), writer.currentImage()->path());
stop = true;
}
bool mergedIntoPrevious = false;
if ( (runtimeOffset > lastOffset) && !textRebases.empty() ) {
uint32_t skipAmount = (uint32_t)(runtimeOffset - lastOffset);
if ( (textRebases.back().repeatCount == 1) && (textRebases.back().skipCount == 0) ) {
textRebases.back().repeatCount = 2;
textRebases.back().skipCount = skipAmount;
mergedIntoPrevious = true;
}
else if ( textRebases.back().skipCount == skipAmount ) {
textRebases.back().repeatCount += 1;
mergedIntoPrevious = true;
}
}
if ( !mergedIntoPrevious ) {
Image::TextFixupPattern pattern;
pattern.target.raw = 0;
pattern.startVmOffset = (uint32_t)runtimeOffset;
pattern.repeatCount = 1;
pattern.skipCount = 0;
textRebases.push_back(pattern);
}
lastOffset = runtimeOffset;
});
writer.setTextRebaseInfo(textRebases);
}
}
void ClosureBuilder::forEachBind(BuilderLoadedImage& forImage, void (^handler)(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop),
void (^strongHandler)(const char* strongSymbolName))
{
__block int lastLibOrdinal = 256;
__block const char* lastSymbolName = nullptr;
__block uint64_t lastAddend = 0;
__block Image::ResolvedSymbolTarget target;
__block ResolvedTargetInfo targetInfo;
forImage.loadAddress()->forEachBind(_diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
if ( (symbolName == lastSymbolName) && (libOrdinal == lastLibOrdinal) && (addend == lastAddend) ) {
handler(runtimeOffset, target, targetInfo, stop);
}
else if ( findSymbol(forImage, libOrdinal, symbolName, weakImport, addend, target, targetInfo) ) {
handler(runtimeOffset, target, targetInfo, stop);
lastSymbolName = symbolName;
lastLibOrdinal = libOrdinal;
lastAddend = addend;
}
else {
stop = true;
}
}, ^(const char* symbolName) {
strongHandler(symbolName);
});
}
void ClosureBuilder::addBindInfo(ImageWriter& writer, BuilderLoadedImage& forImage)
{
const uint32_t ptrSize = forImage.loadAddress()->pointerSize();
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::BindPattern, binds, 512);
__block uint64_t lastOffset = -ptrSize;
__block Image::ResolvedSymbolTarget lastTarget = { {0, 0} };
forEachBind(forImage, ^(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop) {
if ( targetInfo.weakBindCoalese ) {
for (Image::BindPattern& aBind : binds) {
if ( (aBind.startVmOffset == runtimeOffset) && (aBind.repeatCount == 1) && (aBind.skipCount == 0) ) {
aBind.target = target;
return;
}
}
}
bool mergedIntoPrevious = false;
if ( !mergedIntoPrevious && (target == lastTarget) && (runtimeOffset > lastOffset) && !binds.empty() ) {
uint64_t skipAmount = (runtimeOffset - lastOffset - ptrSize)/ptrSize;
if ( skipAmount*ptrSize != (runtimeOffset - lastOffset - ptrSize) ) {
}
else {
if ( (binds.back().repeatCount == 1) && (binds.back().skipCount == 0) && (skipAmount <= 255) ) {
binds.back().repeatCount = 2;
binds.back().skipCount = skipAmount;
assert(binds.back().skipCount == skipAmount); mergedIntoPrevious = true;
}
else if ( (binds.back().skipCount == skipAmount) && (binds.back().repeatCount < 0xfff) ) {
uint32_t prevRepeatCount = binds.back().repeatCount;
binds.back().repeatCount += 1;
assert(binds.back().repeatCount > prevRepeatCount); mergedIntoPrevious = true;
}
}
}
if ( (target == lastTarget) && (runtimeOffset == lastOffset) && !binds.empty() ) {
mergedIntoPrevious = true;
}
if ( !mergedIntoPrevious ) {
Image::BindPattern pattern;
pattern.target = target;
pattern.startVmOffset = runtimeOffset;
pattern.repeatCount = 1;
pattern.skipCount = 0;
assert(pattern.startVmOffset == runtimeOffset);
binds.push_back(pattern);
}
lastTarget = target;
lastOffset = runtimeOffset;
}, ^(const char* strongSymbolName) {
if ( !_makingDyldCacheImages ) {
Image::ResolvedSymbolTarget strongOverride;
ResolvedTargetInfo strongTargetInfo;
if ( findSymbolInImage(forImage.loadAddress(), strongSymbolName, 0, false, strongOverride, strongTargetInfo) ) {
for (const BuilderLoadedImage& li : _loadedImages) {
if ( li.loadAddress()->inDyldCache() && li.loadAddress()->hasWeakDefs() ) {
Image::ResolvedSymbolTarget implInCache;
ResolvedTargetInfo implInCacheInfo;
if ( findSymbolInImage(li.loadAddress(), strongSymbolName, 0, false, implInCache, implInCacheInfo) ) {
Closure::PatchEntry patch;
patch.exportCacheOffset = (uint32_t)implInCache.sharedCache.offset;
patch.overriddenDylibInCache = li.imageNum;
patch.replacement = strongOverride;
_weakDefCacheOverrides.push_back(patch);
}
}
}
}
}
});
writer.setBindInfo(binds);
}
void ClosureBuilder::reportRebasesAndBinds(ImageWriter& writer, BuilderLoadedImage& forImage)
{
forImage.loadAddress()->forEachRebase(_diag, true, ^(uint64_t runtimeOffset, bool& stop) {
_handlers->rebase(forImage.imageNum, forImage.loadAddress(), (uint32_t)runtimeOffset);
});
forEachBind(forImage, ^(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop) {
_handlers->bind(forImage.imageNum, forImage.loadAddress(), (uint32_t)runtimeOffset, target, targetInfo);
},
^(const char* strongSymbolName) {});
if ( forImage.loadAddress()->cputype == CPU_TYPE_I386 ) {
}
}
static const char* sTreatAsWeak[] = {
"__Znwm", "__ZnwmRKSt9nothrow_t",
"__Znam", "__ZnamRKSt9nothrow_t",
"__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm",
"__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm",
"__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t",
"__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t",
"__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t",
"__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t"
};
void ClosureBuilder::addChainedFixupInfo(ImageWriter& writer, const BuilderLoadedImage& forImage)
{
__block uint32_t dataPageCount = 1;
forImage.loadAddress()->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
if ( info.protections & VM_PROT_WRITE ) {
dataPageCount += ((info.fileSize+4095) / 4096);
}
});
STACK_ALLOC_ARRAY(uint64_t, starts, dataPageCount);
forImage.loadAddress()->forEachChainedFixupStart(_diag, ^(uint64_t runtimeOffset, bool& stop) {
starts.push_back(runtimeOffset);
});
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ResolvedSymbolTarget, targets, 1024);
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(ResolvedTargetInfo, targetInfos, 1024);
forImage.loadAddress()->forEachChainedFixupTarget(_diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
Image::ResolvedSymbolTarget target;
ResolvedTargetInfo targetInfo;
if ( !findSymbol(forImage, libOrdinal, symbolName, weakImport, addend, target, targetInfo) ) {
const char* expectedInPath = forImage.loadAddress()->dependentDylibLoadPath(libOrdinal-1);
_diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, forImage.path());
stop = true;
return;
}
if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
bool alreadyInArray = false;
for (const char* sym : _weakDefsFromChainedBinds) {
if ( strcmp(sym, symbolName) == 0 ) {
alreadyInArray = true;
break;
}
}
if ( !alreadyInArray )
_weakDefsFromChainedBinds.push_back(symbolName);
}
targets.push_back(target);
targetInfos.push_back(targetInfo);
});
if ( _diag.hasError() )
return;
if ( _handlers != nullptr )
_handlers->chainedBind(forImage.imageNum, forImage.loadAddress(), starts, targets, targetInfos);
else
writer.setChainedFixups(starts, targets);
if ( _isLaunchClosure && forImage.loadAddress()->hasWeakDefs() && forImage.loadAddress()->isMainExecutable() ) {
for (const char* weakSymbolName : sTreatAsWeak) {
Diagnostics exportDiag;
dyld3::MachOAnalyzer::FoundSymbol foundInfo;
if ( forImage.loadAddress()->findExportedSymbol(exportDiag, weakSymbolName, foundInfo, nullptr) ) {
_weakDefsFromChainedBinds.push_back(weakSymbolName);
}
}
}
}
bool ClosureBuilder::findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports,
Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
{
targetInfo.foundInDylib = nullptr;
targetInfo.requestedSymbolName = symbolName;
targetInfo.addend = addend;
targetInfo.isWeakDef = false;
MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) {
return (const MachOLoaded*)findDependent(mh, depIndex);
};
MachOAnalyzer::DependentToMachOLoaded finder = nullptr;
if ( followReExports )
finder = reexportFinder;
dyld3::MachOAnalyzer::FoundSymbol foundInfo;
if ( macho->findExportedSymbol(_diag, symbolName, foundInfo, finder) ) {
const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib;
targetInfo.foundInDylib = foundInfo.foundInDylib;
targetInfo.foundSymbolName = foundInfo.foundSymbolName;
if ( foundInfo.isWeakDef )
targetInfo.isWeakDef = true;
if ( foundInfo.kind == MachOAnalyzer::FoundSymbol::Kind::absolute ) {
target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
target.absolute.value = foundInfo.value + addend;
}
else if ( impDylib->inDyldCache() ) {
target.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache;
target.sharedCache.offset = (uint8_t*)impDylib - (uint8_t*)_dyldCache + foundInfo.value + addend;
}
else {
target.image.kind = Image::ResolvedSymbolTarget::kindImage;
target.image.imageNum = findLoadedImage(impDylib).imageNum;
target.image.offset = foundInfo.value + addend;
}
return true;
}
return false;
}
bool ClosureBuilder::findSymbol(const BuilderLoadedImage& fromImage, int libOrdinal, const char* symbolName, bool weakImport, uint64_t addend,
Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
{
targetInfo.weakBindCoalese = false;
targetInfo.weakBindSameImage = false;
targetInfo.requestedSymbolName = symbolName;
targetInfo.libOrdinal = libOrdinal;
if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
for (const BuilderLoadedImage& li : _loadedImages) {
if ( !li.rtldLocal && findSymbolInImage(li.loadAddress(), symbolName, addend, true, target, targetInfo) )
return true;
}
if ( weakImport ) {
target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
target.absolute.value = 0;
return true;
}
_diag.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName, fromImage.path());
}
else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
bool foundWeakDefImpl = false;
bool foundStrongDefImpl = false;
bool foundImpl = false;
Image::ResolvedSymbolTarget aTarget;
ResolvedTargetInfo aTargetInfo;
STACK_ALLOC_ARRAY(const BuilderLoadedImage*, cachedDylibsUsingSymbol, 1024);
for (const BuilderLoadedImage& li : _loadedImages) {
if ( li.loadAddress()->hasWeakDefs() && !li.rtldLocal ) {
if ( findSymbolInImage(li.loadAddress(), symbolName, addend, false, aTarget, aTargetInfo) ) {
foundImpl = true;
if ( !li.loadAddress()->hasChainedFixups() && (aTargetInfo.foundInDylib == fromImage.loadAddress()) )
targetInfo.weakBindSameImage = true;
if ( aTargetInfo.isWeakDef ) {
if ( !foundWeakDefImpl && !foundStrongDefImpl ) {
target = aTarget;
targetInfo = aTargetInfo;
}
foundWeakDefImpl = true;
}
else {
if ( !foundStrongDefImpl ) {
target = aTarget;
targetInfo = aTargetInfo;
}
foundStrongDefImpl = true;
}
}
if ( foundImpl && !_makingDyldCacheImages && li.loadAddress()->inDyldCache() )
cachedDylibsUsingSymbol.push_back(&li);
}
}
if ( !cachedDylibsUsingSymbol.empty() ) {
for (const BuilderLoadedImage* li : cachedDylibsUsingSymbol) {
Image::ResolvedSymbolTarget implInCache;
ResolvedTargetInfo implInCacheInfo;
if ( findSymbolInImage(li->loadAddress(), symbolName, addend, false, implInCache, implInCacheInfo) ) {
if ( implInCache != target ) {
Closure::PatchEntry patch;
patch.exportCacheOffset = (uint32_t)implInCache.sharedCache.offset;
patch.overriddenDylibInCache = li->imageNum;
patch.replacement = target;
_weakDefCacheOverrides.push_back(patch);
}
}
}
}
targetInfo.weakBindCoalese = true;
if ( foundImpl )
return true;
_diag.error("symbol '%s' not found, expected to be weak-def coalesced", symbolName);
}
else {
const BuilderLoadedImage* targetLoadedImage = nullptr;
if ( (libOrdinal > 0) && (libOrdinal <= (int)fromImage.dependents.count()) ) {
ImageNum childNum = fromImage.dependents[libOrdinal - 1].imageNum();
if ( childNum != kMissingWeakLinkedImage ) {
targetLoadedImage = &findLoadedImage(childNum);
}
}
else if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
targetLoadedImage = &fromImage;
}
else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
targetLoadedImage = &_loadedImages[_mainProgLoadIndex];
}
else {
_diag.error("unknown special ordinal %d in %s", libOrdinal, fromImage.path());
return false;
}
if ( targetLoadedImage != nullptr ) {
if ( findSymbolInImage(targetLoadedImage->loadAddress(), symbolName, addend, true, target, targetInfo) )
return true;
}
if ( weakImport ) {
target.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
target.absolute.value = 0;
return true;
}
const char* expectedInPath = targetLoadedImage ? targetLoadedImage->path() : "unknown";
_diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, fromImage.path());
if ( _launchErrorInfo != nullptr ) {
_launchErrorInfo->kind = DYLD_EXIT_REASON_SYMBOL_MISSING;
_launchErrorInfo->clientOfDylibPath = fromImage.path();
_launchErrorInfo->targetDylibPath = expectedInPath;
_launchErrorInfo->symbol = symbolName;
}
}
return false;
}
void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError)
{
if ( initInfos[loadIndex].visited )
return;
initInfos[loadIndex].visited = true;
initInfos[loadIndex].danglingUpward = false;
if (_loadedImages[loadIndex].isBadImage) {
hasError = true;
return;
}
for (const Image::LinkedImage& dep : _loadedImages[loadIndex].dependents) {
if ( dep.imageNum() == kMissingWeakLinkedImage )
continue;
ClosureBuilder::BuilderLoadedImage& depLi = findLoadedImage(dep.imageNum());
uint32_t depLoadIndex = (uint32_t)_loadedImages.index(depLi);
if ( dep.kind() == Image::LinkKind::upward ) {
if ( !initInfos[depLoadIndex].visited )
initInfos[depLoadIndex].danglingUpward = true;
}
else {
depthFirstRecurseSetInitInfo(depLoadIndex, initInfos, initOrder, hasError);
if (hasError)
return;
}
}
initInfos[loadIndex].initOrder = initOrder++;
}
void ClosureBuilder::computeInitOrder(ImageWriter& imageWriter, uint32_t loadIndex)
{
InitInfo initInfos[_loadedImages.count()];
bzero(initInfos, sizeof(initInfos));
uint32_t initOrder = 1;
bool hasMissingDependent = false;
depthFirstRecurseSetInitInfo(loadIndex, initInfos, initOrder, hasMissingDependent);
if (hasMissingDependent) {
imageWriter.setInvalid();
return;
}
for (uint32_t i=0; i < (uint32_t)_loadedImages.count(); ++i) {
if ( !initInfos[i].visited && initInfos[i].danglingUpward ) {
depthFirstRecurseSetInitInfo(i, initInfos, initOrder, hasMissingDependent);
}
}
if (hasMissingDependent) {
imageWriter.setInvalid();
return;
}
STACK_ALLOC_ARRAY(uint32_t, indexOfImagesWithInits, _loadedImages.count());
uint32_t index = 0;
for (const BuilderLoadedImage& li : _loadedImages) {
if ( initInfos[index].visited && li.hasInits ) {
indexOfImagesWithInits.push_back(index);
}
++index;
}
if ( indexOfImagesWithInits.count() > 1 ) {
for (uint32_t i=0; i < indexOfImagesWithInits.count()-1; ++i) {
for (uint32_t j=0; j < indexOfImagesWithInits.count()-i-1; ++j) {
if ( initInfos[indexOfImagesWithInits[j]].initOrder > initInfos[indexOfImagesWithInits[j+1]].initOrder ) {
uint32_t temp = indexOfImagesWithInits[j];
indexOfImagesWithInits[j] = indexOfImagesWithInits[j+1];
indexOfImagesWithInits[j+1] = temp;
}
}
}
}
ImageNum initNums[indexOfImagesWithInits.count()];
for (uint32_t i=0; i < indexOfImagesWithInits.count(); ++i) {
initNums[i] = _loadedImages[indexOfImagesWithInits[i]].imageNum;
}
imageWriter.setInitsOrder(initNums, (uint32_t)indexOfImagesWithInits.count());
}
void ClosureBuilder::addCachePatchInfo(ImageWriter& imageWriter, const BuilderLoadedImage& forImage)
{
assert(_handlers != nullptr);
_handlers->forEachExportsPatch(forImage.imageNum, ^(const CacheDylibsBindingHandlers::PatchInfo& info) {
assert(info.usesCount != 0);
imageWriter.addExportPatchInfo(info.exportCacheOffset, info.exportSymbolName, info.usesCount, info.usesArray);
});
}
void ClosureBuilder::addClosureInfo(LaunchClosureWriter& closureWriter)
{
assert(_libSystemImageNum != 0);
closureWriter.setLibSystemImageNum(_libSystemImageNum);
assert(_libDyldImageNum != 0);
Image::ResolvedSymbolTarget entryLocation;
ResolvedTargetInfo entryInfo;
if ( findSymbolInImage(findLoadedImage(_libDyldImageNum).loadAddress(), "__ZN5dyld318entryVectorForDyldE", 0, false, entryLocation, entryInfo) ) {
const dyld3::LibDyldEntryVector* libDyldEntry = nullptr;
switch ( entryLocation.image.kind ) {
case Image::ResolvedSymbolTarget::kindSharedCache:
libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)_dyldCache + entryLocation.sharedCache.offset);
break;
case Image::ResolvedSymbolTarget::kindImage:
libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)findLoadedImage(entryLocation.image.imageNum).loadAddress() + entryLocation.image.offset);
break;
}
if ( (libDyldEntry != nullptr) && (libDyldEntry->binaryFormatVersion == dyld3::closure::kFormatVersion) )
closureWriter.setLibDyldEntry(entryLocation);
else
_diag.error("libdyld.dylib entry vector is incompatible");
}
else {
_diag.error("libdyld.dylib is missing entry vector");
}
ImageNum mainProgImageNum = _loadedImages[_mainProgLoadIndex].imageNum;
closureWriter.setTopImageNum(mainProgImageNum);
uint32_t entryOffset;
bool usesCRT;
if ( _loadedImages[_mainProgLoadIndex].loadAddress()->getEntry(entryOffset, usesCRT) ) {
Image::ResolvedSymbolTarget location;
location.image.kind = Image::ResolvedSymbolTarget::kindImage;
location.image.imageNum = mainProgImageNum;
location.image.offset = entryOffset;
if ( usesCRT )
closureWriter.setStartEntry(location);
else
closureWriter.setMainEntry(location);
}
_pathOverrides.forEachEnvVar(^(const char* envVar) {
closureWriter.addEnvVar(envVar);
});
STACK_ALLOC_ARRAY(const char*, paths, 8192);
if ( _mustBeMissingPaths != nullptr ) {
_mustBeMissingPaths->forEachPath(^(const char* aPath) {
paths.push_back(aPath);
});
}
closureWriter.setMustBeMissingFiles(paths);
}
const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures)
{
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0);
const mach_header* mainMH = (const mach_header*)fileInfo.fileContent;
BuilderLoadedImage loadImagesStorage[512];
Image::LinkedImage dependenciesStorage[512*8];
InterposingTuple tuplesStorage[64];
Closure::PatchEntry cachePatchStorage[64];
const char* weakDefNameStorage[64];
_loadedImages.setInitialStorage(loadImagesStorage, 512);
_dependencies.setInitialStorage(dependenciesStorage, 512*8);
_interposingTuples.setInitialStorage(tuplesStorage, 64);
_weakDefCacheOverrides.setInitialStorage(cachePatchStorage, 64);
_weakDefsFromChainedBinds.setInitialStorage(weakDefNameStorage, 64);
ArrayFinalizer<BuilderLoadedImage> scopedCleanup(_loadedImages, ^(BuilderLoadedImage& li) { if (li.unmapWhenDone) {_fileSystem.unloadFile(li.loadedFileInfo); li.unmapWhenDone=false;} });
const MachOAnalyzer* mainExecutable = MachOAnalyzer::validMainExecutable(_diag, mainMH, fileInfo.path, fileInfo.sliceLen, _archName, _platform);
if ( mainExecutable == nullptr )
return nullptr;
if ( !mainExecutable->isDynamicExecutable() ) {
_diag.error("not a main executable");
return nullptr;
}
_isLaunchClosure = true;
_nextIndex = 0;
_pathOverrides.forEachInsertedDylib(^(const char* dylibPath) {
BuilderLoadedImage insertEntry;
insertEntry.loadedFileInfo.path = strdup_temp(dylibPath);
insertEntry.imageNum = _startImageNum + _nextIndex++;
insertEntry.unmapWhenDone = true;
insertEntry.contentRebased = false;
insertEntry.hasInits = false;
insertEntry.markNeverUnload = true;
insertEntry.rtldLocal = false;
insertEntry.isBadImage = false;
insertEntry.overrideImageNum = 0;
_loadedImages.push_back(insertEntry);
});
_mainProgLoadIndex = (uint32_t)_loadedImages.count();
BuilderLoadedImage mainEntry;
mainEntry.loadedFileInfo = fileInfo;
mainEntry.imageNum = _startImageNum + _nextIndex++;
mainEntry.unmapWhenDone = false;
mainEntry.contentRebased = false;
mainEntry.hasInits = false;
mainEntry.markNeverUnload = true;
mainEntry.rtldLocal = false;
mainEntry.isBadImage = false;
mainEntry.overrideImageNum = 0;
_loadedImages.push_back(mainEntry);
LoadedImageChain chainStart = { nullptr, _loadedImages[_mainProgLoadIndex] };
recursiveLoadDependents(chainStart);
if ( _diag.hasError() )
return nullptr;
for (uint32_t i=0; i < _mainProgLoadIndex; ++i) {
closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, _loadedImages[i].loadedFileInfo.path, _archName, _platform);
const char* originalLoadPath = _loadedImages[i].loadedFileInfo.path;
_loadedImages[i].loadedFileInfo = loadedFileInfo;
if ( _loadedImages[i].loadAddress() != nullptr ) {
LoadedImageChain insertChainStart = { nullptr, _loadedImages[i] };
recursiveLoadDependents(insertChainStart);
}
if ( _diag.hasError() || (_loadedImages[i].loadAddress() == nullptr) ) {
if ( !allowInsertFailures ) {
if ( _diag.noError() )
_diag.error("could not load inserted dylib %s", originalLoadPath);
return nullptr;
}
_diag.clearError(); _loadedImages.remove(i);
i -= 1;
_mainProgLoadIndex -= 1;
_nextIndex -= 1;
for (uint32_t j=i+1; j < _loadedImages.count(); ++j) {
if ( (_loadedImages[j].imageNum >= _startImageNum) && (_loadedImages[j].imageNum <= _startImageNum+_nextIndex) )
_loadedImages[j].imageNum -= 1;
}
}
}
loadDanglingUpwardLinks();
STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
for (BuilderLoadedImage& li : _loadedImages) {
if ( li.imageNum >= _startImageNum ) {
writers.push_back(ImageWriter());
buildImage(writers.back(), li);
if ( _diag.hasError() )
return nullptr;
}
if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) )
_libDyldImageNum = li.imageNum;
else if ( strcmp(li.path(), "/usr/lib/libSystem.B.dylib") == 0 )
_libSystemImageNum = li.imageNum;
}
for (uint32_t i=0; i <= _mainProgLoadIndex; ++i)
computeInitOrder(writers[i], i);
ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
for (ImageWriter& writer : writers) {
imageArrayWriter.appendImage(writer.finalize());
writer.deallocate();
}
const ImageArray* imageArray = imageArrayWriter.finalize();
__block LaunchClosureWriter closureWriter(imageArray);
if ( _dyldCache != nullptr ) {
uuid_t cacheUUID;
_dyldCache->getUUID(cacheUUID);
closureWriter.setDyldCacheUUID(cacheUUID);
for (const BuilderLoadedImage& li : _loadedImages) {
if ( li.overrideImageNum != 0 ) {
const Image* cacheImage = _dyldImageArray->imageForNum(li.overrideImageNum);
STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, cacheImage->patchableExportCount());
MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) {
return (const MachOLoaded*)findDependent(mh, depIndex);
};
cacheImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* symbolName) {
dyld3::MachOAnalyzer::FoundSymbol foundInfo;
Diagnostics patchDiag;
Closure::PatchEntry patch;
patch.overriddenDylibInCache = li.overrideImageNum;
patch.exportCacheOffset = cacheOffsetOfImpl;
if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, reexportFinder) ) {
const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib;
patch.replacement.image.kind = Image::ResolvedSymbolTarget::kindImage;
patch.replacement.image.imageNum = findLoadedImage(impDylib).imageNum;
patch.replacement.image.offset = foundInfo.value;
}
else {
patch.replacement.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
patch.replacement.absolute.value = 0;
}
patches.push_back(patch);
});
closureWriter.addCachePatches(patches);
}
}
if ( !_weakDefsFromChainedBinds.empty() ) {
for (const char* symbolName : _weakDefsFromChainedBinds) {
Image::ResolvedSymbolTarget cacheOverrideTarget;
bool haveCacheOverride = false;
bool foundCachOverrideIsWeakDef = false;
for (const BuilderLoadedImage& li : _loadedImages) {
if ( !li.loadAddress()->hasWeakDefs() )
continue;
Image::ResolvedSymbolTarget target;
ResolvedTargetInfo targetInfo;
if ( findSymbolInImage(li.loadAddress(), symbolName, 0, false, target, targetInfo) ) {
if ( li.loadAddress()->inDyldCache() ) {
if ( haveCacheOverride ) {
Closure::PatchEntry patch;
patch.exportCacheOffset = (uint32_t)target.sharedCache.offset;
patch.overriddenDylibInCache = li.imageNum;
patch.replacement = cacheOverrideTarget;
_weakDefCacheOverrides.push_back(patch);
}
else {
break;
}
}
else {
if ( !haveCacheOverride || (foundCachOverrideIsWeakDef && !targetInfo.isWeakDef) ) {
cacheOverrideTarget = target;
foundCachOverrideIsWeakDef = targetInfo.isWeakDef;
haveCacheOverride = true;
}
}
}
}
}
}
if ( !_weakDefCacheOverrides.empty() )
closureWriter.addCachePatches(_weakDefCacheOverrides);
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED
char bootSessionUUID[256] = { 0 };
size_t bootSize = sizeof(bootSessionUUID);
if ( sysctlbyname("kern.bootsessionuuid", bootSessionUUID, &bootSize, NULL, 0) == 0 )
closureWriter.setBootUUID(bootSessionUUID);
#endif
imageArray->forEachImage(^(const Image* image, bool &stop) {
if ( !image->inDyldCache() )
addInterposingTuples(closureWriter, image, findLoadedImage(image->imageNum()).loadAddress());
});
closureWriter.applyInterposing();
closureWriter.setUsedAtPaths(_atPathUsed);
closureWriter.setUsedFallbackPaths(_fallbackPathUsed);
closureWriter.setInitImageCount((uint32_t)_loadedImages.count());
addClosureInfo(closureWriter);
const LaunchClosure* result = closureWriter.finalize();
imageArrayWriter.deallocate();
return result;
}
const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const LaunchClosure* mainClosure, const Array<LoadedImage>& alreadyLoadedList,
closure::ImageNum callerImageNum, bool noLoad, bool canUseSharedCacheClosure, closure::ImageNum* topImageNum)
{
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0);
BuilderLoadedImage loadImagesStorage[512];
Image::LinkedImage dependenciesStorage[512*8];
Closure::PatchEntry cachePatchStorage[64];
_loadedImages.setInitialStorage(loadImagesStorage, 512);
_dependencies.setInitialStorage(dependenciesStorage, 512*8);
_weakDefCacheOverrides.setInitialStorage(cachePatchStorage, 64);
ArrayFinalizer<BuilderLoadedImage> scopedCleanup(_loadedImages, ^(BuilderLoadedImage& li) { if (li.unmapWhenDone) {_fileSystem.unloadFile(li.loadedFileInfo); li.unmapWhenDone=false;} });
bool cachedDylibsExpectedOnDisk = _dyldCache ? _dyldCache->header.dylibsExpectedOnDisk : true;
uintptr_t callerImageIndex = UINTPTR_MAX;
for (const LoadedImage& ali : alreadyLoadedList) {
const Image* image = ali.image();
const MachOAnalyzer* ma = (MachOAnalyzer*)(ali.loadedAddress());
bool inDyldCache = ma->inDyldCache();
BuilderLoadedImage entry;
ImageNum overrideImageNum;
entry.loadedFileInfo.path = image->path();
entry.loadedFileInfo.fileContent = ma;
entry.loadedFileInfo.sliceOffset = 0;
entry.loadedFileInfo.inode = 0;
entry.loadedFileInfo.mtime = 0;
entry.imageNum = image->imageNum();
entry.dependents = image->dependentsArray();
entry.unmapWhenDone = false;
entry.contentRebased = inDyldCache;
entry.hasInits = false;
entry.markNeverUnload = image->neverUnload();
entry.rtldLocal = ali.hideFromFlatSearch();
entry.isBadImage = false;
entry.overrideImageNum = 0;
if ( !inDyldCache && image->isOverrideOfDyldCacheImage(overrideImageNum) ) {
entry.overrideImageNum = overrideImageNum;
canUseSharedCacheClosure = false;
}
if ( !inDyldCache || cachedDylibsExpectedOnDisk )
image->hasFileModTimeAndInode(entry.loadedFileInfo.inode, entry.loadedFileInfo.mtime);
if ( entry.imageNum == callerImageNum )
callerImageIndex = _loadedImages.count();
_loadedImages.push_back(entry);
}
_alreadyInitedIndex = (uint32_t)_loadedImages.count();
_isLaunchClosure = false;
for (uint32_t i=0; i < alreadyLoadedList.count(); ++i) {
if ( _loadedImages[i].loadAddress()->isMainExecutable() ) {
_mainProgLoadIndex = i;
break;
}
}
BuilderLoadedImage* foundTopImage;
_nextIndex = 0;
BuilderLoadedImage& callerImage = (callerImageIndex != UINTPTR_MAX) ? _loadedImages[callerImageIndex] : _loadedImages[_mainProgLoadIndex];
LoadedImageChain chainCaller = { nullptr, callerImage };
LoadedImageChain chainMain = { &chainCaller, _loadedImages[_mainProgLoadIndex] };
if ( !findImage(path, chainMain, foundTopImage, false, canUseSharedCacheClosure) ) {
if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) ) {
char resolvedPath[PATH_MAX];
if ( _fileSystem.getRealPath(path, resolvedPath) ) {
if ( !findImage(resolvedPath, chainMain, foundTopImage, false, canUseSharedCacheClosure) ) {
return nullptr;
}
} else {
return nullptr;
}
} else {
return nullptr;
}
}
if ( noLoad ) {
if ( (uint32_t)_loadedImages.count() == _alreadyInitedIndex )
*topImageNum = foundTopImage->imageNum;
else
*topImageNum = 0;
return nullptr;
}
if ( (_dyldCache != nullptr) && (_dyldCache->header.cacheType == kDyldSharedCacheTypeProduction) ) {
if ( foundTopImage->imageNum < closure::kFirstLaunchClosureImageNum ) {
*topImageNum = foundTopImage->imageNum;
return nullptr;
}
}
LoadedImageChain chainTopDylib = { &chainMain, *foundTopImage };
recursiveLoadDependents(chainTopDylib);
if ( _diag.hasError() )
return nullptr;
loadDanglingUpwardLinks();
STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
for (BuilderLoadedImage& li : _loadedImages) {
if ( li.imageNum >= _startImageNum ) {
writers.push_back(ImageWriter());
buildImage(writers.back(), li);
}
}
*topImageNum = foundTopImage->imageNum;
if ( writers.count() == 0 ) {
return nullptr;
} else if ( canUseSharedCacheClosure && ( foundTopImage->imageNum < closure::kFirstLaunchClosureImageNum ) ) {
topImageNum = 0;
return sRetryDlopenClosure;
}
computeInitOrder(writers[0], (uint32_t)alreadyLoadedList.count());
ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
for (ImageWriter& writer : writers) {
imageArrayWriter.appendImage(writer.finalize());
writer.deallocate();
}
const ImageArray* imageArray = imageArrayWriter.finalize();
DlopenClosureWriter closureWriter(imageArray);
closureWriter.setTopImageNum(foundTopImage->imageNum);
if ( _dyldCache != nullptr ) {
for (const BuilderLoadedImage& li : _loadedImages) {
if ( (li.overrideImageNum != 0) && (li.imageNum >= _startImageNum) ) {
const Image* cacheImage = _dyldImageArray->imageForNum(li.overrideImageNum);
STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, cacheImage->patchableExportCount());
MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) {
return (const MachOLoaded*)findDependent(mh, depIndex);
};
cacheImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* symbolName) {
dyld3::MachOAnalyzer::FoundSymbol foundInfo;
Diagnostics patchDiag;
Closure::PatchEntry patch;
patch.overriddenDylibInCache = li.overrideImageNum;
patch.exportCacheOffset = cacheOffsetOfImpl;
if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, reexportFinder) ) {
const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib;
patch.replacement.image.kind = Image::ResolvedSymbolTarget::kindImage;
patch.replacement.image.imageNum = findLoadedImage(impDylib).imageNum;
patch.replacement.image.offset = foundInfo.value;
}
else {
patch.replacement.absolute.kind = Image::ResolvedSymbolTarget::kindAbsolute;
patch.replacement.absolute.value = 0;
}
patches.push_back(patch);
});
closureWriter.addCachePatches(patches);
}
}
}
assert(_mustBeMissingPaths == nullptr);
const DlopenClosure* result = closureWriter.finalize();
imageArrayWriter.deallocate();
return result;
}
const LaunchClosure* ClosureBuilder::makeLaunchClosure(const char* mainPath, bool allowInsertFailures)
{
closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, mainPath, _archName, _platform);
const MachOAnalyzer* mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
loadedFileInfo.path = mainPath;
if (_diag.hasError())
return nullptr;
if (mh == nullptr) {
_diag.error("could not load file");
return nullptr;
}
if (!mh->isDynamicExecutable()) {
_diag.error("file is not an executable");
return nullptr;
}
const_cast<PathOverrides*>(&_pathOverrides)->setMainExecutable(mh, mainPath);
const LaunchClosure* launchClosure = makeLaunchClosure(loadedFileInfo, allowInsertFailures);
loadedFileInfo.unload(loadedFileInfo);
return launchClosure;
}
const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases)
{
uintptr_t maxImageCount = dylibs.count() + 16;
_loadedImages.reserve(maxImageCount);
_dependencies.reserve(maxImageCount*16);
_makingDyldCacheImages = true;
_makingCustomerCache = customerCache;
_aliases = &aliases;
__block ImageNum imageNum = _startImageNum;
for (const CachedDylibInfo& aDylibInfo : dylibs) {
BuilderLoadedImage entry;
entry.loadedFileInfo = aDylibInfo.fileInfo;
entry.imageNum = imageNum++;
entry.unmapWhenDone = false;
entry.contentRebased = false;
entry.hasInits = false;
entry.markNeverUnload = true;
entry.rtldLocal = false;
entry.isBadImage = false;
entry.overrideImageNum = 0;
_loadedImages.push_back(entry);
}
for (BuilderLoadedImage& li : _loadedImages) {
LoadedImageChain chainStart = { nullptr, li };
recursiveLoadDependents(chainStart);
if ( _diag.hasError() )
break;
}
assert(_loadedImages.count() == dylibs.count());
STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
for (BuilderLoadedImage& li : _loadedImages) {
writers.push_back(ImageWriter());
buildImage(writers.back(), li);
}
for (const BuilderLoadedImage& li : _loadedImages) {
uint32_t index = li.imageNum - _startImageNum;
computeInitOrder(writers[index], index);
}
for (const BuilderLoadedImage& li : _loadedImages) {
uint32_t index = li.imageNum - _startImageNum;
addCachePatchInfo(writers[index], li);
}
ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
for (ImageWriter& writer : writers) {
imageArrayWriter.appendImage(writer.finalize());
writer.deallocate();
}
const ImageArray* imageArray = imageArrayWriter.finalize();
return imageArray;
}
#if BUILDING_CACHE_BUILDER
const ImageArray* ClosureBuilder::makeOtherDylibsImageArray(const Array<LoadedFileInfo>& otherDylibs, uint32_t cachedDylibsCount)
{
uintptr_t maxImageCount = otherDylibs.count() + cachedDylibsCount + 128;
_loadedImages.reserve(maxImageCount);
_dependencies.reserve(maxImageCount*16);
_nextIndex = 0;
for (const LoadedFileInfo& aDylibInfo : otherDylibs) {
BuilderLoadedImage entry;
entry.loadedFileInfo = aDylibInfo;
entry.imageNum = _startImageNum + _nextIndex++;
entry.unmapWhenDone = false;
entry.contentRebased = false;
entry.hasInits = false;
entry.markNeverUnload = false;
entry.rtldLocal = false;
entry.isBadImage = false;
entry.overrideImageNum = 0;
_loadedImages.push_back(entry);
}
for (uint64_t index = 0; index != _loadedImages.count(); ++index) {
BuilderLoadedImage& li = _loadedImages[index];
LoadedImageChain chainStart = { nullptr, li };
recursiveLoadDependents(chainStart);
if ( _diag.hasError() ) {
_diag.warning("while building dlopen closure for %s: %s", li.loadedFileInfo.path, _diag.errorMessage().c_str());
_diag.clearError();
li.isBadImage = true; }
}
auto invalidateBadImages = [&]() {
while (true) {
bool madeChange = false;
for (BuilderLoadedImage& li : _loadedImages) {
if (li.isBadImage) {
continue;
}
for (Image::LinkedImage depIndex : li.dependents) {
if ( depIndex.imageNum() == kMissingWeakLinkedImage )
continue;
if ( depIndex.imageNum() < dyld3::closure::kLastDyldCacheImageNum )
continue;
BuilderLoadedImage& depImage = findLoadedImage(depIndex.imageNum());
if (depImage.isBadImage) {
_diag.warning("while building dlopen closure for %s: dependent dylib had error", li.loadedFileInfo.path);
li.isBadImage = true; madeChange = true;
}
}
}
if (!madeChange)
break;
}
};
invalidateBadImages();
STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
for (BuilderLoadedImage& li : _loadedImages) {
if ( li.imageNum == 0 ) {
writers.push_back(ImageWriter());
writers.back().setInvalid();
continue;
}
if ( li.imageNum < dyld3::closure::kLastDyldCacheImageNum )
continue;
writers.push_back(ImageWriter());
buildImage(writers.back(), li);
if ( _diag.hasError() ) {
_diag.warning("while building dlopen closure for %s: %s", li.loadedFileInfo.path, _diag.errorMessage().c_str());
_diag.clearError();
li.isBadImage = true; writers.back().setInvalid();
}
}
invalidateBadImages();
for (const BuilderLoadedImage& li : _loadedImages) {
if ( li.imageNum < dyld3::closure::kLastDyldCacheImageNum )
continue;
if (li.isBadImage)
continue;
uint32_t index = li.imageNum - _startImageNum;
computeInitOrder(writers[index], index);
}
ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
for (ImageWriter& writer : writers) {
imageArrayWriter.appendImage(writer.finalize());
writer.deallocate();
}
const ImageArray* imageArray = imageArrayWriter.finalize();
return imageArray;
}
#endif
bool ClosureBuilder::inLoadedImageArray(const Array<LoadedImage>& loadedList, ImageNum imageNum)
{
for (const LoadedImage& ali : loadedList) {
if ( ali.image()->representsImageNum(imageNum) )
return true;
}
return false;
}
void ClosureBuilder::buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Image* image)
{
STACK_ALLOC_ARRAY(const Image*, needToRecurse, 256);
image->forEachDependentImage(^(uint32_t dependentIndex, dyld3::closure::Image::LinkKind kind, ImageNum depImageNum, bool &stop) {
if ( !inLoadedImageArray(loadedList, depImageNum) ) {
const Image* depImage = ImageArray::findImage(imagesArrays, depImageNum);
loadedList.push_back(LoadedImage::make(depImage));
needToRecurse.push_back(depImage);
}
});
for (const Image* img : needToRecurse) {
buildLoadOrderRecurse(loadedList, imagesArrays, img);
}
}
void ClosureBuilder::buildLoadOrder(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Closure* toAdd)
{
const dyld3::closure::Image* topImage = ImageArray::findImage(imagesArrays, toAdd->topImage());
loadedList.push_back(LoadedImage::make(topImage));
buildLoadOrderRecurse(loadedList, imagesArrays, topImage);
}
} }