#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/mount.h>
#include "ImageLoader.h"
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;
// 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);
}
}
}
+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)
{
}
}
-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() )
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 ) {
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
}
}
-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);
// 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);
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;
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
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
int result2 = close(src);
if ( (result1 != 0) || (result2 != 0) )
throw "can't close file";
+
+ return freespace;
}
#include <mach/mach_time.h> // struct mach_timebase_info
#include <stdint.h>
#include <vector>
+#include <set>
#include "mach-o/dyld_gdb.h"
// 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;
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;
// 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;
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
= 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
kSharedRegionLoadFileState,
kSharedRegionMapFileState,
kSharedRegionMapFilePrivateState,
+ kSharedRegionMapFilePrivateMMapState,
kSharedRegionMapFilePrivateOutsideState,
};
static SharedRegionState sSharedRegionState = kSharedRegionStartState;
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;
}
}
}
- 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;
}
}
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();
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
}
}
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;
}
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;
}
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);
}
// 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]);
// 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;
}
}
}
}
+
+ // 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)
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);
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);
(*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 ) {
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;
}
}
}
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);
_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;
}
}
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);
}
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);
}
// 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);
}
}
+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
{
if ( dyld::gLogAPIs )
fprintf(stderr, "%s()\n", __func__);
-
+
// list of requested dylibs actually loaded
std::vector<ImageLoader*> preboundImages;
struct timeval currentTime = { 0 , 0 };
gettimeofday(¤tTime, 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) {
--- /dev/null
+##
+# 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
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}