Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928/* * Copyright (c) 2017 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */
#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>#if !TARGET_OS_SIMULATOR && !TARGET_OS_DRIVERKIT#include <sandbox.h>#include <sandbox/private.h>#endif//#include <dispatch/dispatch.h>#include <mach/vm_page_size.h>
#include "MachOFile.h"#include "MachOLoaded.h"#include "MachOAnalyzer.h"#include "Logging.h"#include "Loading.h"#include "Tracing.h"#include "dyld2.h"#include "dyld_cache_format.h"
#include "objc-shared-cache.h"
namespace dyld { void log(const char* m, ...);}
namespace {
// utility to track a set of ImageNum's in useclass 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 anonymous
namespace dyld3 {
Loader::Loader(const Array<LoadedImage>& existingImages, Array<LoadedImage>& newImagesStorage, const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays, const closure::ObjCSelectorOpt* selOpt, const Array<closure::Image::ObjCSelectorImage>& selImages, LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDofs) : _existingImages(existingImages), _newImages(newImagesStorage), _imagesArrays(imagesArrays), _dyldCacheAddress(cacheAddress), _dyldCacheSelectorOpt(nullptr), _closureSelectorOpt(selOpt), _closureSelectorImages(selImages), _logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDofs){#if BUILDING_DYLD // This is only needed for dyld and the launch closure, not the dlopen closures if ( _dyldCacheAddress != nullptr ) { _dyldCacheSelectorOpt = ((const DyldSharedCache*)_dyldCacheAddress)->objcOpt()->selopt(); }#endif}
void Loader::addImage(const LoadedImage& li){ _newImages.push_back(li);}
LoadedImage* Loader::findImage(closure::ImageNum targetImageNum){#if BUILDING_DYLD // The launch images are different in dyld vs libdyld. In dyld, the new images are // the launch images, while in libdyld, the existing images are the launch images if (LoadedImage* info = _launchImagesCache.findImage(targetImageNum, _newImages)) { return info; }
for (uint64_t index = 0; index != _newImages.count(); ++index) { LoadedImage& info = _newImages[index]; if ( info.image()->representsImageNum(targetImageNum) ) { // Try cache this entry for next time _launchImagesCache.tryAddImage(targetImageNum, index); return &info; } }#elif BUILDING_LIBDYLD for (const LoadedImage& info : _existingImages) { if ( info.image()->representsImageNum(targetImageNum) ) return (LoadedImage*)&info; } for (LoadedImage& info : _newImages) { if ( info.image()->representsImageNum(targetImageNum) ) return &info; }#else#error Must be building dyld or libdyld#endif 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, bool& someCacheImageOverridden){ // accumulate all image overrides STACK_ALLOC_ARRAY(ImageOverride, overrides, _existingImages.maxCount() + _newImages.maxCount()); for (const auto anArray : _imagesArrays) { // ignore prebuilt Image* in dyld cache if ( anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum ) continue; anArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) { ImageOverride overrideEntry; if ( image->isOverrideOfDyldCacheImage(overrideEntry.inCache) ) { someCacheImageOverridden = true; overrideEntry.replacement = image->imageNum(); overrides.push_back(overrideEntry); } }); }
// make cache for fast lookup of already loaded images __block ImageNumSet alreadyLoaded; for (const LoadedImage& info : _existingImages) { alreadyLoaded.add(info.image()->imageNum()); } alreadyLoaded.add(_newImages.begin()->image()->imageNum());
// for each image in _newImages, starting at the top image, make sure its dependents are in _allImages uintptr_t index = 0; while ( (index < _newImages.count()) && diag.noError() ) { const closure::Image* image = _newImages[index].image(); //fprintf(stderr, "completeAllDependents(): looking at dependents of %s\n", image->path()); image->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& stop) { // check if imageNum needs to be changed to an override for (const ImageOverride& entry : overrides) { if ( entry.inCache == depImageNum ) { depImageNum = entry.replacement; break; } } // check if this dependent is already loaded if ( !alreadyLoaded.contains(depImageNum) ) { // if not, look in imagesArrays const closure::Image* depImage = closure::ImageArray::findImage(_imagesArrays, depImageNum); if ( depImage != nullptr ) { //dyld::log(" load imageNum=0x%05X, image path=%s\n", depImageNum, depImage->path()); if ( _newImages.freeCount() == 0 ) { diag.error("too many initial images"); stop = true; } else { _newImages.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){ // scan array and map images not already loaded for (LoadedImage& info : _newImages) { if ( info.loadedAddress() != nullptr ) { // log main executable's segments 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 laterReadOnly, 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); }); } } // skip over ones already loaded continue; } if ( info.image()->inDyldCache() ) { if ( info.image()->overridableDylib() ) { struct stat statBuf; if ( stat(info.image()->path(), &statBuf) == 0 ) { // verify file has not changed since closure was built 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; // out of for loop }
} if ( diag.hasError() ) { // bummer, need to clean up by unmapping any images just mapped for (LoadedImage& info : _newImages) { if ( (info.state() == LoadedImage::State::mapped) && !info.image()->inDyldCache() && !info.leaveMapped() ) { _logSegments("dyld: unmapping %s\n", info.image()->path()); unmapImage(info); } } return; }
// apply fixups for (LoadedImage& info : _newImages) { // images in shared cache do not need fixups applied if ( info.image()->inDyldCache() ) continue; // previously loaded images were previously fixed up if ( info.state() < LoadedImage::State::fixedUp ) { applyFixupsToImage(diag, info); if ( diag.hasError() ) break; info.setState(LoadedImage::State::fixedUp); } }
// find and register dtrace DOFs if ( processDOFs ) { STACK_ALLOC_OVERFLOW_SAFE_ARRAY(DOFInfo, dofImages, _newImages.count()); for (LoadedImage& info : _newImages) { 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_OS_SIMULATOR || TARGET_OS_DRIVERKIT // sandbox calls not yet supported in dyld_sim 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);
// open file#if BUILDING_DYLD int fd = dyld::my_open(info.image()->path(), O_RDONLY, 0);#else int fd = ::open(info.image()->path(), O_RDONLY, 0);#endif 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; }
// get file info struct stat statBuf;#if TARGET_OS_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; }
// verify file has not changed since closure was built 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; } }
// handle case on iOS where sliceOffset in closure is wrong because file was thinned after cache was built if ( (_dyldCacheAddress != nullptr) && !(((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk) ) { if ( sliceOffset != 0 ) { if ( round_page_kernel(codeSignFileOffset+codeSignFileSize) == round_page_kernel(statBuf.st_size) ) { // file is now thin sliceOffset = 0; } } }
// register code signature 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; // start of mach-o slice in fat file siginfo.fs_blob_start = (void*)(long)(codeSignFileOffset); // start of CD in mach-o file siginfo.fs_blob_size = codeSignFileSize; // size of CD 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; } }
// <rdar://problem/41015217> dyld should use F_CHECK_LV even on unsigned binaries { // <rdar://problem/32684903> always call F_CHECK_LV to preflight 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; } }
// reserve address range 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());
// map each segment __block bool mmapFailure = false; __block const uint8_t* codeSignatureStartAddress = nullptr; __block const uint8_t* linkeditEndAddress = nullptr; __block bool mappedFirstSegment = false; __block uint64_t maxFileOffset = 0; image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& stop) { // <rdar://problem/32363581> Mapping zero filled segments fails with mmap of size 0 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; } // sanity check first segment is mach-o header 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); } maxFileOffset = fileOffset + fileSize; }); if ( mmapFailure ) { ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize); ::close(fd); return; }
// <rdar://problem/47163421> speculatively read whole slice fspecread_t specread = {} ; specread.fsr_offset = sliceOffset; specread.fsr_length = maxFileOffset; specread.fsr_flags = 0; fcntl(fd, F_SPECULATIVE_READ, &specread); _logSegments("dyld: Speculatively read offset=0x%08llX, len=0x%08llX, path=%s\n", sliceOffset, maxFileOffset, image->path());
// close file close(fd);
#if BUILDING_LIBDYLD // verify file has not changed since closure was built by checking code signature has not changed struct CDHashWrapper { uint8_t cdHash[20]; };
// Get all the hashes for the image STACK_ALLOC_OVERFLOW_SAFE_ARRAY(CDHashWrapper, expectedCDHashes, 1); image->forEachCDHash(^(const uint8_t *cdHash, bool &stop) { CDHashWrapper cdHashWrapper; memcpy(cdHashWrapper.cdHash, cdHash, sizeof(CDHashWrapper::cdHash)); expectedCDHashes.push_back(cdHashWrapper); });
if (!expectedCDHashes.empty()) { if (expectedCDHashes.count() != 1) { // We should only see a single hash for dylibs diag.error("code signature count invalid"); } else if ( codeSignatureStartAddress == nullptr ) { diag.error("code signature missing"); } else if ( codeSignatureStartAddress+codeSignFileSize > linkeditEndAddress ) { diag.error("code signature extends beyond end of __LINKEDIT"); } else { // Get all the cd hashes for the macho STACK_ALLOC_OVERFLOW_SAFE_ARRAY(CDHashWrapper, foundCDHashes, 1); const MachOLoaded* lmo = (MachOLoaded*)loadAddress; lmo->forEachCDHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize, ^(const uint8_t *cdHash) { CDHashWrapper cdHashWrapper; memcpy(cdHashWrapper.cdHash, cdHash, sizeof(CDHashWrapper::cdHash)); foundCDHashes.push_back(cdHashWrapper); });
if (foundCDHashes.empty()) { diag.error("code signature format invalid"); } else if (expectedCDHashes.count() != foundCDHashes.count()) { diag.error("code signature count invalid"); } else { // We found a hash, so make sure its equal. if ( ::memcmp(foundCDHashes[0].cdHash, expectedCDHashes[0].cdHash, 20) != 0 ) diag.error("code signature changed since closure was built"); } } if ( diag.hasError() ) { ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize); return; } }
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR // tell kernel about fairplay encrypted regions 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 { // allocate a buffer on the stack for the variable length dof_ioctl_data_t type uint8_t buffer[sizeof(dof_ioctl_data_t) + dofs.count()*sizeof(dof_helper_t)]; dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer;
// fill in buffer with one dof_helper_t per DOF section 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); }
// tell kernel about all DOF sections en mas // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel user_addr_t val = (user_addr_t)(unsigned long)ioctlData; if ( ioctl(fd, DTRACEHIOC_ADDDOF, &val) != -1 ) { // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field. // Note, the closure marked the image as being never unload, so we don't need to keep the ID around // or support unregistering it later. 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(){#ifdef DARLING return false;#else uint8_t dofEnabled = *((uint8_t*)_COMM_PAGE_DTRACE_DOF_ENABLED); return ( (dofEnabled & 1) );#endif}
void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger){#if __arm__ || __arm64__ // <rdar://problem/29099600> dyld should tell the kernel when it is doing fix-ups caused by roots 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) { // this is a rebase, add slide 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) { // this is a bind, set to target 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 imageOffsetToStartsInfo, const Array<closure::Image::ResolvedSymbolTarget>& targets, bool& stop) { // this is a chain of fixups, fix up all STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128); targetAddrs.reserve(targets.count()); for (uint32_t i=0; i < targets.count(); ++i) targetAddrs.push_back((void*)resolveTarget(targets[i])); ((dyld3::MachOAnalyzer*)(info.loadedAddress()))->withChainStarts(diag, imageOffsetToStartsInfo, ^(const dyld_chained_starts_in_image* starts) { info.loadedAddress()->fixupAllChainedFixups(diag, starts, slide, targetAddrs, ^(void* loc, void* newValue) { _logFixups("dyld: fixup: %s:%p = %p\n", leafName, loc, newValue); }); }); }, ^(uint64_t imageOffsetToFixup) { uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); _logFixups("dyld: fixup objc image info: %s Setting objc image info for precomputed objc\n", leafName);
MachOAnalyzer::ObjCImageInfo *imageInfo = (MachOAnalyzer::ObjCImageInfo *)fixUpLoc; ((MachOAnalyzer::ObjCImageInfo *)imageInfo)->flags |= MachOAnalyzer::ObjCImageInfo::dyldPreoptimized; }, ^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) { // this is a bind, set to target uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind); uintptr_t value = resolveTarget(bindTarget); _logFixups("dyld: fixup objc protocol: %s:%p = %p\n", leafName, fixUpLoc, (void*)value); *fixUpLoc = value; }, ^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) { // fixupObjCSelRefs closure::Image::ResolvedSymbolTarget fixupTarget; if ( inSharedCache ) { const char* selectorString = _dyldCacheSelectorOpt->getEntryForIndex(selectorIndex); fixupTarget.sharedCache.kind = closure::Image::ResolvedSymbolTarget::kindSharedCache; fixupTarget.sharedCache.offset = (uint64_t)selectorString - (uint64_t)_dyldCacheAddress; } else { closure::ImageNum imageNum; uint64_t vmOffset; bool gotLocation = _closureSelectorOpt->getStringLocation(selectorIndex, _closureSelectorImages, imageNum, vmOffset); assert(gotLocation); fixupTarget.image.kind = closure::Image::ResolvedSymbolTarget::kindImage; fixupTarget.image.imageNum = imageNum; fixupTarget.image.offset = vmOffset; }
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); uintptr_t value = resolveTarget(fixupTarget); _logFixups("dyld: fixup objc selector: %s:%p(was '%s') = %p(now '%s')\n", leafName, fixUpLoc, (const char*)*fixUpLoc, (void*)value, (const char*)value); *fixUpLoc = value; }, ^(uint64_t imageOffsetToFixup, bool &stop) { // fixupObjCStableSwift // Class really is stable Swift, pretending to be pre-stable. // Fix its lie. uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup); uintptr_t value = ((*fixUpLoc) | MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_STABLE) & ~MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_LEGACY; _logFixups("dyld: fixup objc stable Swift: %s:%p = %p\n", leafName, fixUpLoc, (void*)value); *fixUpLoc = value; }, ^(uint64_t imageOffsetToFixup, bool &stop) { // TODO: Implement this });
#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) { // FIXME }); if ( segmentsMadeWritable ) setSegmentProtects(info, false);#endif
// make any read-only data segments read-only if ( image->hasReadOnlyData() && !image->inDyldCache() ) { image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& segStop) { if ( laterReadOnly ) { ::mprotect((void*)(imageLoadAddress+vmOffset), (size_t)vmSize, VM_PROT_READ); } }); }
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 laterReadOnly, 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_DYLDLoadedImage* Loader::LaunchImagesCache::findImage(closure::ImageNum imageNum, Array<LoadedImage>& images) const { if ( (imageNum < _firstImageNum) || (imageNum >= _lastImageNum) ) return nullptr;
uint64_t cacheIndex = imageNum - _firstImageNum; uint32_t imagesIndex = _imageIndices[cacheIndex]; if ( imagesIndex == 0 ) return nullptr;
// Note the index is offset by 1 so that 0's are not yet set return &images[imagesIndex - 1];}
void Loader::LaunchImagesCache::tryAddImage(closure::ImageNum imageNum, uint64_t allImagesIndex) { if ( (imageNum < _firstImageNum) || (imageNum >= _lastImageNum) ) return;
uint64_t cacheIndex = imageNum - _firstImageNum;
// Note the index is offset by 1 so that 0's are not yet set _imageIndices[cacheIndex] = (uint32_t)allImagesIndex + 1;}
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); }}
#endif
#if (BUILDING_LIBDYLD || BUILDING_DYLD) bool internalInstall() {#if TARGET_OS_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 }#endif
#if BUILDING_LIBDYLD// hack because libdyld.dylib should not link with libc++.dylibextern "C" void __cxa_pure_virtual() __attribute__((visibility("hidden")));void __cxa_pure_virtual(){ abort();}#endif
} // namespace dyld3