From 0be5d81c6abb58cc8b67e69601836a20d21891aa Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 12 Jul 2005 21:13:57 +0000 Subject: [PATCH] dyld-43.1.tar.gz --- src/ImageLoader.cpp | 78 +++++++--- src/ImageLoader.h | 21 ++- src/ImageLoaderMachO.cpp | 136 ++++++++++++++++-- src/ImageLoaderMachO.h | 3 +- src/dyld.cpp | 26 ++-- src/dyldAPIs.cpp | 66 +++++---- .../dlsym-RTLD_NEXT-missing/Makefile | 48 +++++++ .../test-cases/dlsym-RTLD_NEXT-missing/foo1.c | 28 ++++ .../test-cases/dlsym-RTLD_NEXT-missing/foo2.c | 28 ++++ .../test-cases/dlsym-RTLD_NEXT-missing/foo3.c | 28 ++++ .../test-cases/dlsym-RTLD_NEXT-missing/main.c | 42 ++++++ 11 files changed, 427 insertions(+), 77 deletions(-) create mode 100644 unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile create mode 100644 unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c create mode 100644 unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c create mode 100644 unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c create mode 100644 unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 663ac98..f3d770e 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "ImageLoader.h" @@ -262,21 +264,22 @@ bool ImageLoader::decrementReferenceCount() return ( --fReferenceCount == 0 ); } - -const ImageLoader::Symbol* ImageLoader::resolveSymbol(const char* name, bool searchSelf, ImageLoader** foundIn) const +// private method that handles circular dependencies by only search any image once +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcept(const char* name, std::set& dontSearchImages, ImageLoader** foundIn) const { const ImageLoader::Symbol* sym; // search self - if ( searchSelf ) { + if ( dontSearchImages.count(this) == 0 ) { sym = this->findExportedSymbol(name, NULL, false, foundIn); if ( sym != NULL ) return sym; + dontSearchImages.insert(this); } // search directly dependent libraries for (uint32_t i=0; i < fLibrariesCount; ++i) { ImageLoader* dependentImage = fLibraries[i].image; - if ( dependentImage != NULL ) { + if ( (dependentImage != NULL) && (dontSearchImages.count(dependentImage) == 0) ) { const ImageLoader::Symbol* sym = dependentImage->findExportedSymbol(name, NULL, false, foundIn); if ( sym != NULL ) return sym; @@ -286,10 +289,11 @@ const ImageLoader::Symbol* ImageLoader::resolveSymbol(const char* name, bool sea // search indirectly dependent libraries for (uint32_t i=0; i < fLibrariesCount; ++i) { ImageLoader* dependentImage = fLibraries[i].image; - if ( dependentImage != NULL ) { - const ImageLoader::Symbol* sym = dependentImage->resolveSymbol(name, false, foundIn); + if ( (dependentImage != NULL) && (dontSearchImages.count(dependentImage) == 0) ) { + const ImageLoader::Symbol* sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dontSearchImages, foundIn); if ( sym != NULL ) return sym; + dontSearchImages.insert(dependentImage); } } @@ -297,6 +301,19 @@ const ImageLoader::Symbol* ImageLoader::resolveSymbol(const char* name, bool sea } +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImages(const char* name, ImageLoader** foundIn) const +{ + std::set dontSearchImages; + dontSearchImages.insert(this); // don't search this image + return this->findExportedSymbolInDependentImagesExcept(name, dontSearchImages, foundIn); +} + +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImages(const char* name, ImageLoader** foundIn) const +{ + std::set dontSearchImages; + return this->findExportedSymbolInDependentImagesExcept(name, dontSearchImages, foundIn); +} + void ImageLoader::link(const LinkContext& context, BindingLaziness bindness, InitializerRunning inits, uint32_t notifyCount) { @@ -688,7 +705,7 @@ void ImageLoader::recursiveInitialization(const LinkContext& context) } } -void ImageLoader::reprebindCommit(const LinkContext& context, bool commit) +void ImageLoader::reprebindCommit(const LinkContext& context, bool commit, bool unmapOld) { // do nothing on unprebound images if ( ! this->isPrebindable() ) @@ -704,7 +721,7 @@ void ImageLoader::reprebindCommit(const LinkContext& context, bool commit) throwf("realpath() failed on %s, errno=%d", this->getPath(), errno); } // recreate temp file name - char tempFilePath[PATH_MAX]; + char tempFilePath[strlen(realFilePath)+strlen("_redoprebinding")+2]; ImageLoader::addSuffix(realFilePath, "_redoprebinding", tempFilePath); if ( commit ) { @@ -718,6 +735,9 @@ void ImageLoader::reprebindCommit(const LinkContext& context, bool commit) else throwf("can't swap temporary re-prebound file: rename(%s,%s) returned errno=%d", tempFilePath, realFilePath, errno); } + else if ( unmapOld ) { + this->prebindUnmap(context); + } } else { // something went wrong during prebinding, delete the temp files @@ -725,25 +745,30 @@ void ImageLoader::reprebindCommit(const LinkContext& context, bool commit) } } -void ImageLoader::reprebind(const LinkContext& context, time_t timestamp) +uint64_t ImageLoader::reprebind(const LinkContext& context, time_t timestamp) { // do nothing on unprebound images if ( ! this->isPrebindable() ) - return; + return INT64_MAX; // do nothing if prebinding is up to date if ( this->usablePrebinding(context) ) { if ( context.verbosePrebinding ) fprintf(stderr, "dyld: no need to re-prebind: %s\n", this->getPath()); - return; + return INT64_MAX; + } + // recreate temp file name + char realFilePath[PATH_MAX]; + if ( realpath(this->getPath(), realFilePath) == NULL ) { + throwf("realpath() failed on %s, errno=%d", this->getPath(), errno); } + char tempFilePath[strlen(realFilePath)+strlen("_redoprebinding")+2]; + ImageLoader::addSuffix(realFilePath, "_redoprebinding", tempFilePath); + // make copy of file and map it in - char tempFilePath[PATH_MAX]; - realpath(this->getPath(), tempFilePath); - ImageLoader::addSuffix(this->getPath(), "_redoprebinding", tempFilePath); uint8_t* fileToPrebind; uint64_t fileToPrebindSize; - this->copyAndMap(tempFilePath, &fileToPrebind, &fileToPrebindSize); + uint64_t freespace = this->copyAndMap(tempFilePath, &fileToPrebind, &fileToPrebindSize); // do format specific prebinding this->doPrebinding(context, timestamp, fileToPrebind); @@ -758,10 +783,12 @@ void ImageLoader::reprebind(const LinkContext& context, time_t timestamp) // log if ( context.verbosePrebinding ) - fprintf(stderr, "dyld: re-prebound: %s\n", this->getPath()); + fprintf(stderr, "dyld: re-prebound: %p %s\n", this->machHeader(), this->getPath()); + + return freespace; } -void ImageLoader::copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint64_t* fileToPrebindSize) +uint64_t ImageLoader::copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint64_t* fileToPrebindSize) { // reopen dylib int src = open(this->getPath(), O_RDONLY); @@ -778,16 +805,16 @@ void ImageLoader::copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint int dst = open(tempFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode); if ( dst == -1 ) throw "can't create temp image"; - + // mark source as "don't cache" (void)fcntl(src, F_NOCACHE, 1); // we want to cache the dst because we are about to map it in and modify it // copy permission bits if ( chmod(tempFile, stat_buf.st_mode & 07777) == -1 ) - throw "can't chmod temp image"; + throwf("can't chmod temp image. errno=%d for %s", errno, this->getPath()); if ( chown(tempFile, stat_buf.st_uid, stat_buf.st_gid) == -1) - throw "can't chown temp image"; + throwf("can't chown temp image. errno=%d for %s", errno, this->getPath()); // copy contents ssize_t len; @@ -801,7 +828,8 @@ void ImageLoader::copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint throw "can't allcoate copy buffer"; } while ( (len = read(src, buffer, kBufferSize)) > 0 ) { - write(dst, buffer, len); + if ( write(dst, buffer, len) == -1 ) + throwf("write failure copying dylib errno=%d for %s", errno, this->getPath()); } // map in dst file @@ -810,6 +838,12 @@ void ImageLoader::copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint if ( *fileToPrebind == (uint8_t*)(-1) ) throw "can't mmap temp image"; + // get free space remaining on dst volume + struct statfs statfs_buf; + if ( fstatfs(dst, &statfs_buf) != 0 ) + throwf("can't fstatfs(), errno=%d for %s", errno, tempFile); + uint64_t freespace = statfs_buf.f_bavail * statfs_buf.f_bsize; + // closing notes: // ok to close file after mapped in // ok to throw above without closing file because the throw will terminate update_prebinding @@ -817,6 +851,8 @@ void ImageLoader::copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint int result2 = close(src); if ( (result1 != 0) || (result2 != 0) ) throw "can't close file"; + + return freespace; } diff --git a/src/ImageLoader.h b/src/ImageLoader.h index 609c222..f15177c 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -30,6 +30,7 @@ #include // struct mach_timebase_info #include #include +#include #include "mach-o/dyld_gdb.h" @@ -166,11 +167,11 @@ public: // st_mtime from stat() on file time_t lastModified(); - // image should create prebound version of itself - void reprebind(const LinkContext& context, time_t timestamp); + // image should create prebound version of itself and return freespace remaining on disk + uint64_t reprebind(const LinkContext& context, time_t timestamp); // if 'commit', the prebound version should be swapped in, otherwise deleted - void reprebindCommit(const LinkContext& context, bool commit); + void reprebindCommit(const LinkContext& context, bool commit, bool unmapOld); // only valid for main executables, returns a pointer its entry point virtual void* getMain() const = 0; @@ -206,8 +207,12 @@ public: virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const = 0; // find exported symbol as if imported by this image - // used by RTLD_NEXT and RTLD_SELF - virtual const Symbol* resolveSymbol(const char* name, bool searchSelf, ImageLoader** foundIn) const; + // used by RTLD_NEXT + virtual const Symbol* findExportedSymbolInDependentImages(const char* name, ImageLoader** foundIn) const; + + // find exported symbol as if imported by this image + // used by RTLD_SELF + virtual const Symbol* findExportedSymbolInImageOrDependentImages(const char* name, ImageLoader** foundIn) const; // gets how many symbols are imported by this image virtual uint32_t getImportedSymbolCount() const = 0; @@ -362,6 +367,9 @@ protected: // in mach-o a parent library knows name of sub libraries it re-exports.. virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0; + // file has been reprebound on disk, unmap this file so original file is released + virtual void prebindUnmap(const LinkContext& context) = 0; + static uint32_t fgImagesWithUsedPrebinding; static uint32_t fgTotalRebaseFixups; static uint32_t fgTotalBindFixups; @@ -391,7 +399,8 @@ protected: private: void init(const char* path, uint64_t offsetInFat, dev_t device, ino_t inode, time_t modDate); intptr_t assignSegmentAddresses(const LinkContext& context); - void copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint64_t* fileToPrebindSize); + uint64_t copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint64_t* fileToPrebindSize); + const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, std::set& dontSearchImages, ImageLoader** foundIn) const; bool fHideSymbols; // ignore this image's exported symbols when linking other images diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index 84a3da6..d89b1da 100644 --- a/src/ImageLoaderMachO.cpp +++ b/src/ImageLoaderMachO.cpp @@ -316,6 +316,38 @@ static uintptr_t sNextAltLoadAddress = 0; #endif +static int +_shared_region_map_file_with_mmap( + int fd, // file descriptor to map into shared region + unsigned int regionCount, // number of entres in array of regions + const _shared_region_mapping_np regions[]) // the array of regions to map +{ + // map in each region + for(unsigned int i=0; i < regionCount; ++i) { + void* mmapAddress = (void*)(uintptr_t)(regions[i].address); + size_t size = regions[i].size; + if ( (regions[i].init_prot & VM_PROT_ZF) != 0 ) { + // do nothing already vm_allocate() which zero fills + } + else { + int protection = 0; + if ( regions[i].init_prot & VM_PROT_EXECUTE ) + protection |= PROT_EXEC; + if ( regions[i].init_prot & VM_PROT_READ ) + protection |= PROT_READ; + if ( regions[i].init_prot & VM_PROT_WRITE ) + protection |= PROT_WRITE; + off_t offset = regions[i].file_offset; + //fprintf(stderr, "mmap(%p, 0x%08lX, block=0x%08X, %s\n", mmapAddress, size, biggestDiff, fPath); + mmapAddress = mmap(mmapAddress, size, protection, MAP_FILE | MAP_FIXED | MAP_PRIVATE, fd, offset); + if ( mmapAddress == ((void*)(-1)) ) + throw "mmap error"; + } + } + + return 0; +} + static bool @@ -437,6 +469,7 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF kSharedRegionLoadFileState, kSharedRegionMapFileState, kSharedRegionMapFilePrivateState, + kSharedRegionMapFilePrivateMMapState, kSharedRegionMapFilePrivateOutsideState, }; static SharedRegionState sSharedRegionState = kSharedRegionStartState; @@ -447,7 +480,15 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF if ( kSharedRegionStartState == sSharedRegionState ) { if ( hasSharedRegionMapFile() ) { - if ( (context.sharedRegionMode == kUsePrivateSharedRegion) || context.slideAndPackDylibs ) { + if ( context.slideAndPackDylibs ) { + sharedRegionMakePrivate(context); + // remove underlying submap and block out 0x90000000 to 0xAFFFFFFF + vm_address_t addr = (vm_address_t)0x90000000; + vm_deallocate(mach_task_self(), addr, 0x20000000); + vm_allocate(mach_task_self(), &addr, 0x20000000, false); + sSharedRegionState = kSharedRegionMapFilePrivateMMapState; + } + else if ( context.sharedRegionMode == kUsePrivateSharedRegion ) { sharedRegionMakePrivate(context); sSharedRegionState = kSharedRegionMapFilePrivateState; } @@ -476,8 +517,8 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF } } - if ( kSharedRegionMapFilePrivateState == sSharedRegionState ) { - if ( 0 != sharedRegionMapFilePrivate(fd, offsetInFat, lenInFat, fileLen, context) ) { + if ( (kSharedRegionMapFilePrivateState == sSharedRegionState) || (kSharedRegionMapFilePrivateMMapState == sSharedRegionState) ) { + if ( 0 != sharedRegionMapFilePrivate(fd, offsetInFat, lenInFat, fileLen, context, (kSharedRegionMapFilePrivateMMapState == sSharedRegionState)) ) { sSharedRegionState = kSharedRegionMapFilePrivateOutsideState; } } @@ -608,12 +649,14 @@ ImageLoaderMachO::sharedRegionMapFile(int fd, return r; } + int ImageLoaderMachO::sharedRegionMapFilePrivate(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, - const LinkContext& context) + const LinkContext& context, + bool usemmap) { const unsigned int segmentCount = fSegments.size(); @@ -650,7 +693,11 @@ ImageLoaderMachO::sharedRegionMapFilePrivate(int fd, uint64_t slide = 0; // try map it in privately (don't allow sliding if we pre-calculated the load address to pack dylibs) - int r = _shared_region_map_file_np(fd, mappingTableCount, mappingTable, context.slideAndPackDylibs ? NULL : &slide); + int r; + if ( usemmap ) + r = _shared_region_map_file_with_mmap(fd, mappingTableCount, mappingTable); + else + r = _shared_region_map_file_np(fd, mappingTableCount, mappingTable, context.slideAndPackDylibs ? NULL : &slide); if ( 0 == r ) { if ( 0 != slide ) { slide = (slide) & (-4096); // round down to page boundary @@ -702,7 +749,7 @@ ImageLoaderMachO::sharedRegionMapFilePrivate(int fd, } } if ( context.slideAndPackDylibs && (r != 0) ) - throw "can't rebase split-seg dylib"; + throwf("can't rebase split-seg dylib %s because shared_region_map_file_np() returned %d", this->getPath(), r); return r; } @@ -891,8 +938,9 @@ void ImageLoaderMachO::parseLoadCmds() fModInitSection = sect; else if ( type == S_MOD_TERM_FUNC_POINTERS ) fModTermSection = sect; - else if ( isDataSeg && (strcmp(sect->sectname, "__dyld") == 0) ) - fDATAdyld = sect; + else if ( isDataSeg && (strcmp(sect->sectname, "__dyld") == 0) ) { + fDATAdyld = sect; + } else if ( isDataSeg && (strcmp(sect->sectname, "__image_notify") == 0) ) fImageNotifySection = sect; } @@ -906,6 +954,12 @@ void ImageLoaderMachO::parseLoadCmds() fDylibID = (struct dylib_command*)cmd; } break; + case LC_LOAD_WEAK_DYLIB: + // do nothing, just prevent LC_REQ_DYLD exception from occuring + break; + default: + if ( (cmd->cmd & LC_REQ_DYLD) != 0 ) + throwf("unknown required load command 0x%08X", cmd->cmd); } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } @@ -1205,7 +1259,7 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) // cache this value that is used in the following loop register const uintptr_t slide = this->fSlide; - + // loop through all local (internal) relocation records const uintptr_t relocBase = this->getRelocBase(); const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); @@ -2296,13 +2350,14 @@ void ImageLoaderMachO::applyPrebindingToLinkEdit(const LinkContext& context, uin // walk all exports and slide their n_value struct macho_nlist* lastExport = &symbolTable[dysymtab->iextdefsym+dysymtab->nextdefsym]; for (struct macho_nlist* entry = &symbolTable[dysymtab->iextdefsym]; entry < lastExport; ++entry) { - entry->n_value += fSlide; + if ( (entry->n_type & N_TYPE) == N_SECT ) + entry->n_value += fSlide; } // walk all local symbols and slide their n_value struct macho_nlist* lastLocal = &symbolTable[dysymtab->ilocalsym+dysymtab->nlocalsym]; for (struct macho_nlist* entry = &symbolTable[dysymtab->ilocalsym]; entry < lastLocal; ++entry) { - if ( (entry->n_type & N_TYPE) == N_SECT ) + if ( entry->n_sect != NO_SECT ) entry->n_value += fSlide; } @@ -2327,10 +2382,69 @@ void ImageLoaderMachO::applyPrebindingToLinkEdit(const LinkContext& context, uin } } } + + // if multi-module, fix up objc_addr (10.4 and later runtime does not use this, but we want to keep file checksum consistent) + if ( dysymtab->nmodtab != 0 ) { + dylib_module* const modulesStart = (struct dylib_module*)(&fileToPrebind[dysymtab->modtaboff]); + dylib_module* const modulesEnd = &modulesStart[dysymtab->nmodtab]; + for (dylib_module* module=modulesStart; module < modulesEnd; ++module) { + if ( module->objc_module_info_size != 0 ) { + module->objc_module_info_addr += fSlide; + } + } + } +} +// file on disk has been reprebound, but we are still mapped to old file +void ImageLoaderMachO::prebindUnmap(const LinkContext& context) +{ + // this removes all mappings to the old file, so the kernel will unlink (delete) it. + // We need to leave the load commands and __LINKEDIT in place + for (std::vector::iterator it=fSegments.begin(); it != fSegments.end(); ++it) { + void* segmentAddress = (void*)((*it)->getActualLoadAddress()); + uintptr_t segmentSize = (*it)->getSize(); + //fprintf(stderr, "unmapping segment %s at %p for %s\n", (*it)->getName(), segmentAddress, this->getPath()); + // save load commands at beginning of __TEXT segment + if ( segmentAddress == fMachOData ) { + // typically load commands are one or two pages in size, so ok to alloc on stack + uint32_t loadCmdSize = sizeof(macho_header) + ((macho_header*)fMachOData)->sizeofcmds; + uint32_t loadCmdPages = (loadCmdSize+4095) & (-4096); + uint8_t loadcommands[loadCmdPages]; + memcpy(loadcommands, fMachOData, loadCmdPages); + // unmap whole __TEXT segment + munmap((void*)(fMachOData), segmentSize); + // allocate and copy back mach_header and load commands + vm_address_t addr = (vm_address_t)fMachOData; + int r2 = vm_allocate(mach_task_self(), &addr, loadCmdPages, false /*at this address*/); + if ( r2 != 0 ) + fprintf(stderr, "prebindUnmap() vm_allocate for __TEXT %d failed\n", loadCmdPages); + memcpy((void*)fMachOData, loadcommands, loadCmdPages); + //fprintf(stderr, "copying back load commands to %p size=%u for %s\n", segmentAddress, loadCmdPages, this->getPath()); + } + else if ( strcmp((*it)->getName(), "__LINKEDIT") == 0 ) { + uint32_t linkEditSize = segmentSize; + uint32_t linkEditPages = (linkEditSize+4095) & (-4096); + void* linkEditTmp = malloc(linkEditPages); + memcpy(linkEditTmp, segmentAddress, linkEditPages); + // unmap whole __LINKEDIT segment + munmap(segmentAddress, segmentSize); + vm_address_t addr = (vm_address_t)segmentAddress; + int r2 = vm_allocate(mach_task_self(), &addr, linkEditPages, false /*at this address*/); + if ( r2 != 0 ) + fprintf(stderr, "prebindUnmap() vm_allocate for __LINKEDIT %d failed\n", linkEditPages); + memcpy(segmentAddress, linkEditTmp, linkEditPages); + //fprintf(stderr, "copying back __LINKEDIT to %p size=%u for %s\n", segmentAddress, linkEditPages, this->getPath()); + free(linkEditTmp); + } + else { + // unmap any other segment + munmap((void*)(segmentAddress), (*it)->getSize()); + } + } } + SegmentMachO::SegmentMachO(const struct macho_segment_command* cmd, ImageLoaderMachO* image, const uint8_t* fileData) : fImage(image), fSize(cmd->vmsize), fFileSize(cmd->filesize), fFileOffset(cmd->fileoff), fPreferredLoadAddress(cmd->vmaddr), fVMProtection(cmd->initprot), fHasFixUps(false), fUnMapOnDestruction(false) diff --git a/src/ImageLoaderMachO.h b/src/ImageLoaderMachO.h index acafd58..8665c96 100644 --- a/src/ImageLoaderMachO.h +++ b/src/ImageLoaderMachO.h @@ -96,6 +96,7 @@ protected: virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const; virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const; virtual bool isPrebindable() const; + virtual void prebindUnmap(const LinkContext& context); #if !__LP64__ // split segs not supported for 64-bits virtual void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); @@ -121,7 +122,7 @@ private: int sharedRegionLoadFile(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); int sharedRegionMapFile(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); int sharedRegionMakePrivate(const LinkContext& context); - int sharedRegionMapFilePrivate(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); + int sharedRegionMapFilePrivate(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context, bool usemmap); int sharedRegionMapFilePrivateOutside(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); void initMappingTable(uint64_t offsetInFat, sf_mapping *mappingTable, uintptr_t baseAddress); void initMappingTable(uint64_t offsetInFat, _shared_region_mapping_np *mappingTable); diff --git a/src/dyld.cpp b/src/dyld.cpp index 1be0973..c747bbd 100644 --- a/src/dyld.cpp +++ b/src/dyld.cpp @@ -259,6 +259,15 @@ void removeImage(ImageLoader* image) (*it)(image->machHeader(), image->getSlide()); } + // tell all interested images + for (std::vector::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) { + dyld_image_info info; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getPath(); + info.imageFileModDate = image->lastModified(); + (*it)->doNotification(dyld_image_removing, 1, &info); + } + // remove from master list for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { if ( *it == image ) { @@ -503,8 +512,9 @@ void processDyldEnvironmentVarible(const char* key, const char* value) sEnv.DYLD_ROOT_PATH = parseColonList(value); for (int i=0; sEnv.DYLD_ROOT_PATH[i] != NULL; ++i) { if ( sEnv.DYLD_ROOT_PATH[i][0] != '/' ) { - fprintf(stderr, "dyld: warning DYLD_ROOT_PATH not used because it contains a non-absolute path"); + fprintf(stderr, "dyld: warning DYLD_ROOT_PATH not used because it contains a non-absolute path\n"); sEnv.DYLD_ROOT_PATH = NULL; + break; } } } @@ -653,7 +663,7 @@ static void checkEnvironmentVariables(const char* envp[]) if ( equals != NULL ) { const char* value = &equals[1]; const int keyLen = equals-keyEqualsValue; - char key[keyLen]; + char key[keyLen+1]; strncpy(key, keyEqualsValue, keyLen); key[keyLen] = '\0'; processDyldEnvironmentVarible(key, value); @@ -1860,20 +1870,16 @@ uintptr_t _main(const struct mach_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[]) { // Pickup the pointer to the exec path. - const char* executable = apple[0]; - if ( executable[0] == '/' ) { - // have full path, use it - sExecPath = executable; - } - else { + sExecPath = apple[0]; + if ( sExecPath[0] != '/' ) { // have relative path, use cwd to make absolute char cwdbuff[MAXPATHLEN]; if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { // maybe use static buffer to avoid calling malloc so early... - char* s = new char[strlen(cwdbuff) + strlen(executable) + 2]; + char* s = new char[strlen(cwdbuff) + strlen(sExecPath) + 2]; strcpy(s, cwdbuff); strcat(s, "/"); - strcat(s, executable); + strcat(s, sExecPath); sExecPath = s; } } diff --git a/src/dyldAPIs.cpp b/src/dyldAPIs.cpp index fb9f89c..7620fe6 100644 --- a/src/dyldAPIs.cpp +++ b/src/dyldAPIs.cpp @@ -1394,7 +1394,7 @@ void* dlsym(void* handle, const char* symbolName) if ( handle == RTLD_NEXT ) { void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - sym = callerImage->resolveSymbol(underscoredName, false, &image); + sym = callerImage->findExportedSymbolInDependentImages(underscoredName, &image); // don't search image, but do search what it links against if ( sym != NULL ) { return (void*)image->getExportedSymbolAddress(sym); } @@ -1409,11 +1409,7 @@ void* dlsym(void* handle, const char* symbolName) if ( handle == RTLD_SELF ) { void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - sym = callerImage->findExportedSymbol(underscoredName, NULL, false, &image); // search first in calling image - if ( sym != NULL ) { - return (void*)image->getExportedSymbolAddress(sym); - } - sym = callerImage->resolveSymbol(underscoredName, false, &image); // search what calling image links against + sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, &image); // search image and what it links against if ( sym != NULL ) { return (void*)image->getExportedSymbolAddress(sym); } @@ -1427,12 +1423,7 @@ void* dlsym(void* handle, const char* symbolName) // real handle image = (ImageLoader*)handle; if ( dyld::validImage(image) ) { - ImageLoader* foundIn; - sym = image->findExportedSymbol(underscoredName, NULL, true, &foundIn); - if ( sym != NULL ) { - return (void*)(foundIn->getExportedSymbolAddress(sym)); - } - sym = image->resolveSymbol(underscoredName, false, &image);// search what image links against + sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, &image); // search image and what it links against if ( sym != NULL ) { return (void*)image->getExportedSymbolAddress(sym); } @@ -1448,6 +1439,24 @@ void* dlsym(void* handle, const char* symbolName) } +static void commitRepreboundFiles(std::vector files, bool unmapOld) +{ + // tell file system to flush all dirty buffers to disk + // after this sync, the _redoprebinding files will be on disk + sync(); + + // now commit (swap file) for each re-prebound image + // this only updates directories, since the files have already been flushed by previous sync() + for (std::vector::iterator it=files.begin(); it != files.end(); it++) { + (*it)->reprebindCommit(dyld::gLinkContext, true, unmapOld); + } + + // tell file system to flush all dirty buffers to disk + // this should flush out all directory changes caused by the file swapping + sync(); +} + + #define UPDATE_PREBINDING_DRY_RUN 0x00000001 #define UPDATE_PREBINDING_PROGRESS 0x00000002 @@ -1460,7 +1469,7 @@ static void _dyld_update_prebinding(int pathCount, const char* paths[], uint32_t { if ( dyld::gLogAPIs ) fprintf(stderr, "%s()\n", __func__); - + // list of requested dylibs actually loaded std::vector preboundImages; @@ -1552,35 +1561,36 @@ static void _dyld_update_prebinding(int pathCount, const char* paths[], uint32_t struct timeval currentTime = { 0 , 0 }; gettimeofday(¤tTime, NULL); time_t timestamp = currentTime.tv_sec; + std::vector updatedImages; for (std::vector::iterator it=preboundImages.begin(); it != preboundImages.end(); it++) { - (*it)->reprebind(dyld::gLinkContext, timestamp); + uint64_t freespace = (*it)->reprebind(dyld::gLinkContext, timestamp); + updatedImages.push_back(*it); if(UPDATE_PREBINDING_PROGRESS & flags) { fprintf(stdout, "update_prebinding: progress: %3u/%u\n", imageNumber, imageCount); fflush(stdout); imageNumber++; } + // see if we are running low on disk space (less than 32MB is "low") + const uint64_t kMinFreeSpace = 32*1024*1024; + if ( freespace < kMinFreeSpace ) { + if ( dyld::gLinkContext.verbosePrebinding || (UPDATE_PREBINDING_DRY_RUN & flags) ) + fprintf(stderr, "update_prebinding: disk space down to %lluMB, committing %lu prebound files\n", freespace/(1024*1024), updatedImages.size()); + // commit files processed so far, to free up more disk space + commitRepreboundFiles(updatedImages, true); + // empty list of temp files + updatedImages.clear(); + } } - // tell file system to flush all dirty buffers to disk - // after this sync, all the _redoprebinding files will be on disk - sync(); - - // now commit (swap file) for each re-prebound image - // this only updates directories, since the files have already been flushed by previous sync() - for (std::vector::iterator it=preboundImages.begin(); it != preboundImages.end(); it++) { - (*it)->reprebindCommit(dyld::gLinkContext, true); - } - - // tell file system to flush all dirty buffers to disk - // this should flush out all directory changes caused by the file swapping - sync(); + // commit them, don't need to unmap old, cause we are done + commitRepreboundFiles(updatedImages, false); } } catch (const char* msg) { // delete temp files try { for (std::vector::iterator it=preboundImages.begin(); it != preboundImages.end(); it++) { - (*it)->reprebindCommit(dyld::gLinkContext, false); + (*it)->reprebindCommit(dyld::gLinkContext, false, false); } } catch (const char* commitMsg) { diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile new file mode 100644 index 0000000..491d9f4 --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2005 Apple Computer, 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ${TESTROOT}/bin/exit-zero-pass.pl "dlsym-RTLD_NEXT-missing with circular libraries" "dlsym-RTLD_NEXT-missing with circular libraries" ./main + +all: main + +main : main.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo1.dylib + + +foo1.dylib : foo1.c foo2.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o foo1.dylib foo2.dylib + +foo2.dylib : foo2.c foo3.dylib + ${CC} ${CCFLAGS} -dynamiclib foo2.c -o foo2.dylib foo3.dylib + +foo3.dylib : foo3.c + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o foo1-temp.dylib -install_name foo1.dylib + ${CC} ${CCFLAGS} -dynamiclib foo3.c -o foo3.dylib foo1-temp.dylib + rm foo1-temp.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle foo1.dylib foo2.dylib foo3.dylib + diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c new file mode 100644 index 0000000..eda5424 --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo1() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c new file mode 100644 index 0000000..a17cf3d --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo2() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c new file mode 100644 index 0000000..923e363 --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, 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@ + */ + + +int foo3() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c new file mode 100644 index 0000000..914cca6 --- /dev/null +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This process has no "bar" function, but it does have libraries with a +/// circular dependency. In 10.4.0 this cause dlsym(RTLD_NEXT) to +/// go into an infinite loop. +/// + + + +int main() +{ + dlsym(RTLD_NEXT, "bar"); + return EXIT_SUCCESS; +} -- 2.45.2