#include <bitset>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <uuid/uuid.h>
#include <mach/mach.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <fcntl.h>
#include <sys/dtrace.h>
#include <sys/errno.h>
#include <unistd.h>
#include <System/sys/mman.h>
#include <System/sys/csr.h>
#include <System/machine/cpu_capabilities.h>
#include <bootstrap.h>
#include <CommonCrypto/CommonDigest.h>
#include <sandbox.h>
#include <sandbox/private.h>
#include <dispatch/dispatch.h>
#include <mach/vm_page_size.h>
#include "MachOFile.h"
#include "MachOLoaded.h"
#include "Logging.h"
#include "Loading.h"
#include "Tracing.h"
#include "dyld.h"
#include "dyld_cache_format.h"
namespace dyld {
void log(const char* m, ...);
}
namespace {
class VIS_HIDDEN ImageNumSet
{
public:
void add(dyld3::closure::ImageNum num);
bool contains(dyld3::closure::ImageNum num) const;
private:
std::bitset<5120> _bitmap;
dyld3::OverflowSafeArray<dyld3::closure::ImageNum> _overflowArray;
};
void ImageNumSet::add(dyld3::closure::ImageNum num)
{
if ( num < 5120 )
_bitmap.set(num);
else
_overflowArray.push_back(num);
}
bool ImageNumSet::contains(dyld3::closure::ImageNum num) const
{
if ( num < 5120 )
return _bitmap.test(num);
for (dyld3::closure::ImageNum existingNum : _overflowArray) {
if ( existingNum == num )
return true;
}
return false;
}
}
namespace dyld3 {
Loader::Loader(Array<LoadedImage>& storage, const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDofs)
: _allImages(storage), _imagesArrays(imagesArrays), _dyldCacheAddress(cacheAddress),
_logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDofs)
{
}
void Loader::addImage(const LoadedImage& li)
{
_allImages.push_back(li);
}
LoadedImage* Loader::findImage(closure::ImageNum targetImageNum)
{
for (LoadedImage& info : _allImages) {
if ( info.image()->representsImageNum(targetImageNum) )
return &info;
}
return nullptr;
}
uintptr_t Loader::resolveTarget(closure::Image::ResolvedSymbolTarget target)
{
const LoadedImage* info;
switch ( target.sharedCache.kind ) {
case closure::Image::ResolvedSymbolTarget::kindSharedCache:
assert(_dyldCacheAddress != nullptr);
return (uintptr_t)_dyldCacheAddress + (uintptr_t)target.sharedCache.offset;
case closure::Image::ResolvedSymbolTarget::kindImage:
info = findImage(target.image.imageNum);
assert(info != nullptr);
return (uintptr_t)(info->loadedAddress()) + (uintptr_t)target.image.offset;
case closure::Image::ResolvedSymbolTarget::kindAbsolute:
if ( target.absolute.value & (1ULL << 62) )
return (uintptr_t)(target.absolute.value | 0xC000000000000000ULL);
else
return (uintptr_t)target.absolute.value;
}
assert(0 && "malformed ResolvedSymbolTarget");
return 0;
}
void Loader::completeAllDependents(Diagnostics& diag, uintptr_t topIndex)
{
STACK_ALLOC_ARRAY(ImageOverride, overrides, _allImages.maxCount());
for (const auto anArray : _imagesArrays) {
if ( anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum )
continue;
anArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
ImageOverride overrideEntry;
if ( image->isOverrideOfDyldCacheImage(overrideEntry.inCache) ) {
overrideEntry.replacement = image->imageNum();
overrides.push_back(overrideEntry);
}
});
}
__block ImageNumSet alreadyLoaded;
for (int i=0; i <= topIndex; ++i) {
alreadyLoaded.add(_allImages[i].image()->imageNum());
}
uintptr_t index = topIndex;
while ( (index < _allImages.count()) && diag.noError() ) {
const closure::Image* image = _allImages[index].image();
image->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& stop) {
for (const ImageOverride& entry : overrides) {
if ( entry.inCache == depImageNum ) {
depImageNum = entry.replacement;
break;
}
}
if ( !alreadyLoaded.contains(depImageNum) ) {
const closure::Image* depImage = closure::ImageArray::findImage(_imagesArrays, depImageNum);
if ( depImage != nullptr ) {
if ( _allImages.freeCount() == 0 ) {
diag.error("too many initial images");
stop = true;
}
else {
_allImages.push_back(LoadedImage::make(depImage));
}
alreadyLoaded.add(depImageNum);
}
else {
diag.error("unable to locate imageNum=0x%04X, depIndex=%d of %s", depImageNum, depIndex, image->path());
stop = true;
}
}
});
++index;
}
}
void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI, uintptr_t topIndex)
{
for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
LoadedImage& info = _allImages[i];
if ( info.loadedAddress() != nullptr ) {
if ( (info.loadedAddress()->filetype == MH_EXECUTE) && (info.state() == LoadedImage::State::mapped) ) {
if ( _logSegments("dyld: mapped by kernel %s\n", info.image()->path()) ) {
info.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
uint64_t start = (long)info.loadedAddress() + vmOffset;
uint64_t end = start+vmSize-1;
if ( (segIndex == 0) && (permissions == 0) ) {
start = 0;
}
_logSegments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", info.loadedAddress()->segmentName(segIndex),
(permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
start, end);
});
}
}
continue;
}
if ( info.image()->inDyldCache() ) {
if ( info.image()->overridableDylib() ) {
struct stat statBuf;
if ( stat(info.image()->path(), &statBuf) == 0 ) {
uint64_t inode;
uint64_t mtime;
if ( info.image()->hasFileModTimeAndInode(inode, mtime) ) {
if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
diag.error("dylib file mtime/inode changed since closure was built for '%s'", info.image()->path());
}
}
else {
diag.error("dylib file not expected on disk, must be a root '%s'", info.image()->path());
}
}
else if ( (_dyldCacheAddress != nullptr) && ((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk ) {
diag.error("dylib file missing, was in dyld shared cache '%s'", info.image()->path());
}
}
if ( diag.noError() ) {
info.setLoadedAddress((MachOLoaded*)((uintptr_t)_dyldCacheAddress + info.image()->cacheOffset()));
info.setState(LoadedImage::State::fixedUp);
if ( _logSegments("dyld: Using from dyld cache %s\n", info.image()->path()) ) {
info.image()->forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
_logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", info.loadedAddress()->segmentName(segIndex),
(permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
(long)info.loadedAddress()+(long)vmOffset, (long)info.loadedAddress()+(long)vmOffset+(long)vmSize-1);
});
}
}
}
else {
mapImage(diag, info, fromOFI);
if ( diag.hasError() )
break; }
}
if ( diag.hasError() ) {
for (LoadedImage& info : _allImages) {
if ( (info.state() == LoadedImage::State::mapped) && !info.image()->inDyldCache() && !info.leaveMapped() ) {
_logSegments("dyld: unmapping %s\n", info.image()->path());
unmapImage(info);
}
}
return;
}
for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
LoadedImage& info = _allImages[i];
if ( info.image()->inDyldCache() )
continue;
if ( info.state() < LoadedImage::State::fixedUp ) {
applyFixupsToImage(diag, info);
if ( diag.hasError() )
break;
info.setState(LoadedImage::State::fixedUp);
}
}
if ( processDOFs ) {
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(DOFInfo, dofImages, _allImages.count());
for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
LoadedImage& info = _allImages[i];
info.image()->forEachDOF(info.loadedAddress(), ^(const void* section) {
DOFInfo dofInfo;
dofInfo.dof = section;
dofInfo.imageHeader = info.loadedAddress();
dofInfo.imageShortName = info.image()->leafName();
dofImages.push_back(dofInfo);
});
}
registerDOFs(dofImages);
}
}
bool Loader::sandboxBlocked(const char* path, const char* kind)
{
#if TARGET_IPHONE_SIMULATOR
return false;
#else
sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
return ( sandbox_check(getpid(), kind, filter, path) > 0 );
#endif
}
bool Loader::sandboxBlockedMmap(const char* path)
{
return sandboxBlocked(path, "file-map-executable");
}
bool Loader::sandboxBlockedOpen(const char* path)
{
return sandboxBlocked(path, "file-read-data");
}
bool Loader::sandboxBlockedStat(const char* path)
{
return sandboxBlocked(path, "file-read-metadata");
}
void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
{
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, info.image()->path(), 0, 0);
const closure::Image* image = info.image();
uint64_t sliceOffset = image->sliceOffsetInFile();
const uint64_t totalVMSize = image->vmSizeToMap();
uint32_t codeSignFileOffset;
uint32_t codeSignFileSize;
bool isCodeSigned = image->hasCodeSignature(codeSignFileOffset, codeSignFileSize);
int fd = ::open(info.image()->path(), O_RDONLY, 0);
if ( fd == -1 ) {
int openErr = errno;
if ( (openErr == EPERM) && sandboxBlockedOpen(image->path()) )
diag.error("file system sandbox blocked open(\"%s\", O_RDONLY)", image->path());
else
diag.error("open(\"%s\", O_RDONLY) failed with errno=%d", image->path(), openErr);
return;
}
struct stat statBuf;
#if TARGET_IPHONE_SIMULATOR
if ( stat(image->path(), &statBuf) != 0 ) {
#else
if ( fstat(fd, &statBuf) != 0 ) {
#endif
int statErr = errno;
if ( (statErr == EPERM) && sandboxBlockedStat(image->path()) )
diag.error("file system sandbox blocked stat(\"%s\")", image->path());
else
diag.error("stat(\"%s\") failed with errno=%d", image->path(), statErr);
close(fd);
return;
}
uint64_t inode;
uint64_t mtime;
if ( image->hasFileModTimeAndInode(inode, mtime) ) {
if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
diag.error("file mtime/inode changed since closure was built for '%s'", image->path());
close(fd);
return;
}
}
if ( (_dyldCacheAddress != nullptr) && !(((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk) ) {
if ( sliceOffset != 0 ) {
if ( round_page_kernel(codeSignFileOffset+codeSignFileSize) == round_page_kernel(statBuf.st_size) ) {
sliceOffset = 0;
}
}
}
uint64_t coveredCodeLength = UINT64_MAX;
if ( isCodeSigned ) {
auto sigTimer = ScopedTimer(DBG_DYLD_TIMING_ATTACH_CODESIGNATURE, 0, 0, 0);
fsignatures_t siginfo;
siginfo.fs_file_start = sliceOffset; siginfo.fs_blob_start = (void*)(long)(codeSignFileOffset); siginfo.fs_blob_size = codeSignFileSize; int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
if ( result == -1 ) {
int errnoCopy = errno;
if ( (errnoCopy == EPERM) || (errnoCopy == EBADEXEC) ) {
diag.error("code signature invalid (errno=%d) sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image->path());
}
else {
diag.error("fcntl(fd, F_ADDFILESIGS_RETURN) failed with errno=%d, sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image->path());
}
close(fd);
return;
}
coveredCodeLength = siginfo.fs_file_start;
if ( coveredCodeLength < codeSignFileOffset ) {
diag.error("code signature does not cover entire file up to signature");
close(fd);
return;
}
}
{
fchecklv checkInfo;
char messageBuffer[512];
messageBuffer[0] = '\0';
checkInfo.lv_file_start = sliceOffset;
checkInfo.lv_error_message_size = sizeof(messageBuffer);
checkInfo.lv_error_message = messageBuffer;
int res = fcntl(fd, F_CHECK_LV, &checkInfo);
if ( res == -1 ) {
diag.error("code signature in (%s) not valid for use in process: %s", image->path(), messageBuffer);
close(fd);
return;
}
}
vm_address_t loadAddress = 0;
kern_return_t r = vm_allocate(mach_task_self(), &loadAddress, (vm_size_t)totalVMSize, VM_FLAGS_ANYWHERE);
if ( r != KERN_SUCCESS ) {
diag.error("vm_allocate(size=0x%0llX) failed with result=%d", totalVMSize, r);
close(fd);
return;
}
if ( sliceOffset != 0 )
_logSegments("dyld: Mapping %s (slice offset=%llu)\n", image->path(), sliceOffset);
else
_logSegments("dyld: Mapping %s\n", image->path());
__block bool mmapFailure = false;
__block const uint8_t* codeSignatureStartAddress = nullptr;
__block const uint8_t* linkeditEndAddress = nullptr;
__block bool mappedFirstSegment = false;
image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
if ( fileSize == 0 )
return;
void* segAddress = mmap((void*)(loadAddress+vmOffset), fileSize, permissions, MAP_FIXED | MAP_PRIVATE, fd, sliceOffset+fileOffset);
int mmapErr = errno;
if ( segAddress == MAP_FAILED ) {
if ( mmapErr == EPERM ) {
if ( sandboxBlockedMmap(image->path()) )
diag.error("file system sandbox blocked mmap() of '%s'", image->path());
else
diag.error("code signing blocked mmap() of '%s'", image->path());
}
else {
diag.error("mmap(addr=0x%0llX, size=0x%08X) failed with errno=%d for %s", loadAddress+vmOffset, fileSize, mmapErr, image->path());
}
mmapFailure = true;
stop = true;
}
else if ( codeSignFileOffset > fileOffset ) {
codeSignatureStartAddress = (uint8_t*)segAddress + (codeSignFileOffset-fileOffset);
linkeditEndAddress = (uint8_t*)segAddress + vmSize;
}
if ( (segAddress != MAP_FAILED) && !mappedFirstSegment ) {
mappedFirstSegment = true;
const MachOFile* mf = (MachOFile*)segAddress;
if ( !mf->isMachO(diag, fileSize) ) {
mmapFailure = true;
stop = true;
}
}
if ( !mmapFailure ) {
const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
_logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", lmo->segmentName(segIndex),
(permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
(long)segAddress, (long)segAddress+(long)vmSize-1);
}
});
if ( mmapFailure ) {
::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
::close(fd);
return;
}
close(fd);
#if BUILDING_LIBDYLD
uint8_t cdHashExpected[20];
if ( image->hasCdHash(cdHashExpected) ) {
if ( codeSignatureStartAddress == nullptr ) {
diag.error("code signature missing");
}
else if ( codeSignatureStartAddress+codeSignFileSize > linkeditEndAddress ) {
diag.error("code signature extends beyond end of __LINKEDIT");
}
else {
uint8_t cdHashFound[20];
const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
if ( lmo->cdHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize, cdHashFound) ) {
if ( ::memcmp(cdHashFound, cdHashExpected, 20) != 0 )
diag.error("code signature changed since closure was built");
}
else {
diag.error("code signature format invalid");
}
}
if ( diag.hasError() ) {
::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
return;
}
}
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
uint32_t fpTextOffset;
uint32_t fpSize;
if ( image->isFairPlayEncrypted(fpTextOffset, fpSize) ) {
const mach_header* mh = (mach_header*)loadAddress;
int result = ::mremap_encrypted(((uint8_t*)mh) + fpTextOffset, fpSize, 1, mh->cputype, mh->cpusubtype);
if ( result != 0 ) {
diag.error("could not register fairplay decryption, mremap_encrypted() => %d", result);
::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
return;
}
}
#endif
_logLoads("dyld: load %s\n", image->path());
timer.setData4((uint64_t)loadAddress);
info.setLoadedAddress((MachOLoaded*)loadAddress);
info.setState(LoadedImage::State::mapped);
}
void Loader::unmapImage(LoadedImage& info)
{
assert(info.loadedAddress() != nullptr);
::vm_deallocate(mach_task_self(), (vm_address_t)info.loadedAddress(), (vm_size_t)(info.image()->vmSizeToMap()));
info.setLoadedAddress(nullptr);
}
void Loader::registerDOFs(const Array<DOFInfo>& dofs)
{
if ( dofs.empty() )
return;
int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
if ( fd < 0 ) {
_logDofs("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n");
}
else {
uint8_t buffer[sizeof(dof_ioctl_data_t) + dofs.count()*sizeof(dof_helper_t)];
dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer;
ioctlData->dofiod_count = dofs.count();
for (unsigned int i=0; i < dofs.count(); ++i) {
strlcpy(ioctlData->dofiod_helpers[i].dofhp_mod, dofs[i].imageShortName, DTRACE_MODNAMELEN);
ioctlData->dofiod_helpers[i].dofhp_dof = (uintptr_t)(dofs[i].dof);
ioctlData->dofiod_helpers[i].dofhp_addr = (uintptr_t)(dofs[i].dof);
}
user_addr_t val = (user_addr_t)(unsigned long)ioctlData;
if ( ioctl(fd, DTRACEHIOC_ADDDOF, &val) != -1 ) {
for (unsigned int i=0; i < dofs.count(); ++i) {
_logDofs("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n",
dofs[i].dof, dofs[i].imageShortName, (int)(ioctlData->dofiod_helpers[i].dofhp_dof));
}
}
else {
_logDofs("dyld: ioctl to register dtrace DOF section failed\n");
}
close(fd);
}
}
bool Loader::dtraceUserProbesEnabled()
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
int dof_mode;
size_t dof_mode_size = sizeof(dof_mode);
if ( sysctlbyname("kern.dtrace.dof_mode", &dof_mode, &dof_mode_size, nullptr, 0) == 0 ) {
return ( dof_mode != 0 );
}
return false;
#else
return true;
#endif
}
void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger)
{
#if __arm__ || __arm64__
logger("vm.footprint_suspend=%d\n", suspend);
int newValue = suspend ? 1 : 0;
int oldValue = 0;
size_t newlen = sizeof(newValue);
size_t oldlen = sizeof(oldValue);
sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
#endif
}
void Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info)
{
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_FIXUPS, (uint64_t)info.loadedAddress(), 0, 0);
closure::ImageNum cacheImageNum;
const char* leafName = info.image()->leafName();
const closure::Image* image = info.image();
const uint8_t* imageLoadAddress = (uint8_t*)info.loadedAddress();
uintptr_t slide = info.loadedAddress()->getSlide();
bool overrideOfCache = info.image()->isOverrideOfDyldCacheImage(cacheImageNum);
if ( overrideOfCache )
vmAccountingSetSuspended(true, _logFixups);
image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
*fixUpLoc += slide;
_logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
},
^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool &stop) {
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind);
uintptr_t value = resolveTarget(bindTarget);
_logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
*fixUpLoc = value;
},
^(uint64_t imageOffsetStart, const Array<closure::Image::ResolvedSymbolTarget>& targets, bool& stop) {
image->forEachChainedFixup((void*)imageLoadAddress, imageOffsetStart, ^(uint64_t* fixupLoc, MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stopChain) {
if ( fixupInfo.authRebase.auth ) {
#if __has_feature(ptrauth_calls)
if ( fixupInfo.authBind.bind ) {
closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.authBind.ordinal];
uint64_t targetAddr = resolveTarget(bindTarget);
if (targetAddr != 0)
targetAddr = fixupInfo.signPointer(fixupLoc, targetAddr);
_logFixups("dyld: fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
fixupLoc, (void*)targetAddr, fixupInfo.authBind.diversity, fixupInfo.authBind.addrDiv, fixupInfo.authBind.keyName());
*fixupLoc = targetAddr;
}
else {
uint64_t targetAddr = (uint64_t)imageLoadAddress + fixupInfo.authRebase.target;
targetAddr = fixupInfo.signPointer(fixupLoc, targetAddr);
_logFixups("dyld: fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
fixupLoc, (void*)targetAddr, fixupInfo.authRebase.diversity, fixupInfo.authRebase.addrDiv, fixupInfo.authRebase.keyName());
*fixupLoc = targetAddr;
}
#else
diag.error("malformed chained pointer");
stop = true;
stopChain = true;
#endif
}
else {
if ( fixupInfo.plainRebase.bind ) {
closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.plainBind.ordinal];
uint64_t targetAddr = resolveTarget(bindTarget) + fixupInfo.plainBind.signExtendedAddend();
_logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixupLoc, (void*)targetAddr);
*fixupLoc = targetAddr;
}
else {
uint64_t targetAddr = fixupInfo.plainRebase.signExtendedTarget() + slide;
_logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixupLoc, (void*)slide);
*fixupLoc = targetAddr;
}
}
});
});
#if __i386__
__block bool segmentsMadeWritable = false;
image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool& stop) {
if ( !segmentsMadeWritable )
setSegmentProtects(info, true);
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
*fixUpLoc += slide;
_logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
},
^(uint32_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
});
if ( segmentsMadeWritable )
setSegmentProtects(info, false);
#endif
if ( overrideOfCache )
vmAccountingSetSuspended(false, _logFixups);
}
#if __i386__
void Loader::setSegmentProtects(const LoadedImage& info, bool write)
{
info.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t protections, bool& segStop) {
if ( protections & VM_PROT_WRITE )
return;
uint32_t regionProt = protections;
if ( write )
regionProt = VM_PROT_WRITE | VM_PROT_READ;
kern_return_t r = vm_protect(mach_task_self(), ((uintptr_t)info.loadedAddress())+(uintptr_t)vmOffset, (uintptr_t)vmSize, false, regionProt);
assert( r == KERN_SUCCESS );
});
}
#endif
#if BUILDING_DYLD
void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop))
{
bool stop = false;
const char* const eof = &buffer[bufferLen];
for (const char* s = buffer; s < eof; ++s) {
char lineBuffer[MAXPATHLEN];
char* t = lineBuffer;
char* tEnd = &lineBuffer[MAXPATHLEN];
while ( (s < eof) && (t != tEnd) ) {
if ( *s == '\n' )
break;
*t++ = *s++;
}
*t = '\0';
lineHandler(lineBuffer, stop);
if ( stop )
break;
}
}
void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop))
{
int fd = dyld::my_open(path, O_RDONLY, 0);
if ( fd != -1 ) {
struct stat statBuf;
if ( fstat(fd, &statBuf) == 0 ) {
const char* lines = (const char*)mmap(nullptr, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if ( lines != MAP_FAILED ) {
forEachLineInFile(lines, (size_t)statBuf.st_size, lineHandler);
munmap((void*)lines, (size_t)statBuf.st_size);
}
}
close(fd);
}
}
bool internalInstall()
{
#if TARGET_IPHONE_SIMULATOR
return false;
#elif __IPHONE_OS_VERSION_MIN_REQUIRED
uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
return ( (devFlags & 1) == 1 );
#else
return ( csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0 );
#endif
}
bool bootArgsContains(const char* arg)
{
#if TARGET_IPHONE_SIMULATOR
return false;
#else
if ( !internalInstall() )
return false;
char pathBuffer[MAXPATHLEN+1];
#if __IPHONE_OS_VERSION_MIN_REQUIRED
strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR));
#else
strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR));
#endif
strlcat(pathBuffer, "dyld-bootargs", MAXPATHLEN+1);
__block bool result = false;
forEachLineInFile(pathBuffer, ^(const char* line, bool& stop) {
const char* delim = strchr(line, ':');
if ( delim == nullptr )
return;
char binary[MAXPATHLEN];
char options[MAXPATHLEN];
strlcpy(binary, line, MAXPATHLEN);
binary[delim-line] = '\0';
strlcpy(options, delim+1, MAXPATHLEN);
if ( (strcmp(dyld::getExecutablePath(), binary) == 0) || (strcmp("*", binary) == 0) ) {
result = (strstr(options, arg) != nullptr);
return;
}
});
size_t len;
if ( sysctlbyname("kern.bootargs", NULL, &len, NULL, 0) != 0 )
return false;
char bootArgsBuffer[len];
if ( sysctlbyname("kern.bootargs", bootArgsBuffer, &len, NULL, 0) != 0 )
return false;
return (strstr(bootArgsBuffer, arg) != nullptr);
#endif
}
#endif
#if BUILDING_LIBDYLD
extern "C" void __cxa_pure_virtual() __attribute__((visibility("hidden")));
void __cxa_pure_virtual()
{
abort();
}
#endif
}