]> git.saurik.com Git - apple/dyld.git/commitdiff
dyld-43.1.tar.gz mac-os-x-1042 v43.1
authorApple <opensource@apple.com>
Tue, 12 Jul 2005 21:13:57 +0000 (21:13 +0000)
committerApple <opensource@apple.com>
Tue, 12 Jul 2005 21:13:57 +0000 (21:13 +0000)
src/ImageLoader.cpp
src/ImageLoader.h
src/ImageLoaderMachO.cpp
src/ImageLoaderMachO.h
src/dyld.cpp
src/dyldAPIs.cpp
unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile [new file with mode: 0644]
unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c [new file with mode: 0644]
unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c [new file with mode: 0644]
unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c [new file with mode: 0644]
unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c [new file with mode: 0644]

index 663ac9866b4c0bc626d9c299b2074ad71b8d1cf5..f3d770edf5f0c8388d074eabe0834f42a2459859 100644 (file)
@@ -31,6 +31,8 @@
 #include <sys/types.h>
 #include <sys/stat.h> 
 #include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/mount.h>
 
 #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<const ImageLoader*>& 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<const ImageLoader*> 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<const ImageLoader*> 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;
 }
 
 
index 609c222cff75a7e065be94eeff14f42e24eafeef..f15177c4c4023d4482aa52049bcf2cfcb47d090e 100644 (file)
@@ -30,6 +30,7 @@
 #include <mach/mach_time.h> // struct mach_timebase_info
 #include <stdint.h>
 #include <vector>
+#include <set>
 
 #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<const ImageLoader*>& dontSearchImages, ImageLoader** foundIn) const;
 
 
        bool                                            fHideSymbols;           // ignore this image's exported symbols when linking other images
index 84a3da66b602ee26292f82763f5bbfb8a802b16d..d89b1da9320f82edde4fef18f373d7234935acf5 100644 (file)
@@ -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<class Segment*>::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)
index acafd5845c86065e5e668ff47d275d49a4e266b2..8665c9676c992c438144f0646f410d85911b454e 100644 (file)
@@ -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);
index 1be09730eae6d9655a35b32fe9c9cc0fcbf69d48..c747bbd5c5a69f3b31e3df9ece8999c4a44cf08f 100644 (file)
@@ -259,6 +259,15 @@ void removeImage(ImageLoader* image)
                (*it)(image->machHeader(), image->getSlide());
        }
        
+       // tell all interested images
+       for (std::vector<ImageLoader*>::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<ImageLoader*>::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;
                }
        }
index fb9f89cf1284f13cd94ec2e28280ea7a83dc3a4b..7620fe683f9649479bb9c2c7c1b3fd371ebbb3e3 100644 (file)
@@ -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<ImageLoader*> 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<ImageLoader*>::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<ImageLoader*> preboundImages;
        
@@ -1552,35 +1561,36 @@ static void _dyld_update_prebinding(int pathCount, const char* paths[], uint32_t
                        struct timeval currentTime = { 0 , 0 };
                        gettimeofday(&currentTime, NULL);
                        time_t timestamp = currentTime.tv_sec;
+                       std::vector<ImageLoader*> updatedImages;
                        for (std::vector<ImageLoader*>::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<ImageLoader*>::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<ImageLoader*>::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 (file)
index 0000000..491d9f4
--- /dev/null
@@ -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 (file)
index 0000000..eda5424
--- /dev/null
@@ -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 (file)
index 0000000..a17cf3d
--- /dev/null
@@ -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 (file)
index 0000000..923e363
--- /dev/null
@@ -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 (file)
index 0000000..914cca6
--- /dev/null
@@ -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 <stdio.h>  // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+
+#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;
+}