Loading...
src/dyldAPIs.cpp dyld-43 dyld-46.16
--- dyld/dyld-43/src/dyldAPIs.cpp
+++ dyld/dyld-46.16/src/dyldAPIs.cpp
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <mach/mach.h>
 #include <sys/time.h>
+#include <sys/sysctl.h>
 
 extern "C" mach_port_name_t	task_self_trap(void);  // can't include <System/mach/mach_traps.h> because it is missing extern C
 
@@ -400,12 +401,11 @@
 
 
 static __attribute__((noinline)) 
-const struct mach_header* addImage(const char* path, bool search, bool dontLoad, bool matchInstallName, bool abortOnError)
+const struct mach_header* addImage(void* callerAddress, const char* path, bool search, bool dontLoad, bool matchInstallName, bool abortOnError)
 {
 	ImageLoader*	image = NULL;
 	try {
 		dyld::clearErrorMessage();
-		void* callerAddress = __builtin_return_address(2); // note layers: 2: real client, 1: libSystem glue, 0: dyld API
 		ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
 		dyld::LoadContext context;
 		context.useSearchPaths		= search;
@@ -447,21 +447,24 @@
 	const bool search = ( (options & NSADDIMAGE_OPTION_WITH_SEARCHING) != 0 );
 	const bool matchInstallName = ( (options & NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME) != 0 );
 	const bool abortOnError = ( (options & NSADDIMAGE_OPTION_RETURN_ON_ERROR) == 0 );
-	return addImage(path, search, dontLoad, matchInstallName, abortOnError);
+	void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+	return addImage(callerAddress, path, search, dontLoad, matchInstallName, abortOnError);
 }
 
 bool NSAddLibrary(const char* path)
 {
 	if ( dyld::gLogAPIs )
 		fprintf(stderr, "%s(\"%s\")\n", __func__, path);
-	return (addImage(path, false, false, false, false) != NULL);
+	void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+	return (addImage(callerAddress, path, false, false, false, false) != NULL);
 }
 
 bool NSAddLibraryWithSearching(const char* path)
 {
 	if ( dyld::gLogAPIs )
 		fprintf(stderr, "%s(\"%s\")\n", __func__, path);
-	return (addImage(path, true, false, false, false) != NULL);
+	void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+	return (addImage(callerAddress, path, true, false, false, false) != NULL);
 }
 
 
@@ -1394,7 +1397,7 @@
 	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 +1412,7 @@
 	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 +1426,7 @@
 	// 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);
 		}
@@ -1445,6 +1439,24 @@
 		dlerrorSet("invalid handle passed to dlsym()");
 	}
 	return NULL;
+}
+
+
+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();
 }
 
 
@@ -1460,7 +1472,7 @@
 {
 	if ( dyld::gLogAPIs )
 		fprintf(stderr, "%s()\n", __func__);
-	
+
 	// list of requested dylibs actually loaded
 	std::vector<ImageLoader*> preboundImages;
 	
@@ -1469,7 +1481,10 @@
 		if ( dyld::getImageCount() != 1 )
 			throw "_dyld_update_prebinding cannot be called with dylib already loaded";
 			
-		const uint32_t max_allowed_link_errors = 10;
+		// note that we are prebinding
+		dyld::gLinkContext.prebinding = true;
+
+		const uint32_t max_allowed_link_errors = 100;
 		uint32_t link_error_count = 0;
 		
 		// load and link each dylib
@@ -1501,10 +1516,12 @@
 						stage = "link";
 					fprintf(stderr, "update_prebinding: warning: could not %s %s: %s\n", stage, paths[i], msg);
 				}
-				if ( image != NULL )
+				if ( (image != NULL) && image->isPrebindable() ) // don't count top level dylib being unprebound as an error
 					link_error_count++;
-				if ( link_error_count > max_allowed_link_errors )
-					throw;
+				if ( link_error_count > max_allowed_link_errors ) {
+					fprintf(stderr, "update_prebinding: too many errors (%d) \n", link_error_count);
+					throw "terminating";
+				}
 			}
 		}
 		
@@ -1548,39 +1565,63 @@
 		else {
 			uint32_t imageCount = preboundImages.size();
 			uint32_t imageNumber = 1;
+
+			// on Intel system, update_prebinding is run twice: i386, then emulated ppc
+			// calculate fudge factors so that progress output represents both runs
+			int denomFactor = 1;
+			int numerAddend = 0;
+			if (UPDATE_PREBINDING_PROGRESS & flags) {
+		#if __i386__
+				// i386 half runs first, just double denominator
+				denomFactor = 2;
+		#endif	
+		#if __ppc__
+				// if emulated ppc, double denominator and shift numerator
+				int mib[] = { CTL_KERN, KERN_CLASSIC, getpid() };
+				int is_emulated = 0;
+				size_t len = sizeof(int);
+				int ret = sysctl(mib, 3, &is_emulated, &len, NULL, 0);
+				if ((ret != -1) && is_emulated) {
+					denomFactor = 2;
+					numerAddend = imageCount;
+				}
+		#endif
+			}
+
 			// tell each image to write itself out re-prebound
 			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);
+					fprintf(stdout, "update_prebinding: progress: %3u/%u\n", imageNumber+numerAddend, imageCount*denomFactor);
 					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) {