Loading...
src/dyld.cpp dyld-43 dyld-46.12
--- dyld/dyld-43/src/dyld.cpp
+++ dyld/dyld-46.12/src/dyld.cpp
@@ -1,6 +1,6 @@
 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
  *
- * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -116,6 +116,7 @@
 static cpu_type_t					sHostCPU;
 static cpu_subtype_t				sHostCPUsubtype;
 static ImageLoader*					sMainExecutable = NULL;
+static bool							sAllImagesMightContainUnlinkedImages;	 // necessary until will support dylib unloading
 static std::vector<ImageLoader*>	sAllImages;
 static std::vector<ImageLoader*>	sImageRoots;
 static std::vector<ImageLoader*>	sImageFilesNeedingTermination;
@@ -240,10 +241,6 @@
 
 void removeImage(ImageLoader* image)
 {
-	// flush find-by-address cache
-	if ( sLastImageByAddressCache == image )
-		sLastImageByAddressCache = NULL;
-
 	// if in termination list, pull it out and run terminator
 	for (std::vector<ImageLoader*>::iterator it=sImageFilesNeedingTermination.begin(); it != sImageFilesNeedingTermination.end(); it++) {
 		if ( *it == image ) {
@@ -259,6 +256,15 @@
 		(*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 ) {
@@ -267,6 +273,10 @@
 		}
 	}
 	
+	// flush find-by-address cache
+	if ( sLastImageByAddressCache == image )
+		sLastImageByAddressCache = NULL;
+
 	// if in announcement list, pull it out 
 	for (std::vector<ImageLoader*>::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) {
 		if ( *it == image ) {
@@ -387,37 +397,6 @@
 	return (const char**)result;
 }
 
-/*
- * Library path searching is not done for setuid programs
- * which are not run by the real user.  Futher the
- * evironment varaible for the library path is cleared so
- * that if this program executes a non-set uid program this
- * part of the evironment will not be passed along so that
- * that program also will not have it's libraries searched
- * for.
- */
- static bool riskyUser()
- {
-	static bool checked = false;
-	static bool risky = false;
-	if ( !checked ) {
-		risky = ( getuid() != 0 && (getuid() != geteuid() || getgid() != getegid()) );
-		checked = true;
-	}
-	return risky;
- }
- 
- 
-static bool disableIfBadUser(char* rhs)
-{
-	bool didDisable = false;
-	if ( riskyUser() ) {
-		*rhs ='\0';
-		didDisable = true;
-	}
-	return didDisable;
-}
-
 static void paths_expand_roots(const char **paths, const char *key, const char *val)
 {
 // 	assert(val != NULL);
@@ -482,41 +461,34 @@
 void processDyldEnvironmentVarible(const char* key, const char* value)
 {
 	if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) {
-		if ( !disableIfBadUser((char*)value) )
-			sEnv.DYLD_FRAMEWORK_PATH = parseColonList(value);
+		sEnv.DYLD_FRAMEWORK_PATH = parseColonList(value);
 	}
 	else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
-		if ( !disableIfBadUser((char*)value) )
-			sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = parseColonList(value);
+		sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = parseColonList(value);
 	}
 	else if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) {
-		if ( !disableIfBadUser((char*)value) )
-			sEnv.DYLD_LIBRARY_PATH = parseColonList(value);
+		sEnv.DYLD_LIBRARY_PATH = parseColonList(value);
 	}
 	else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
-		if ( !disableIfBadUser((char*)value) )
-			sEnv.DYLD_FALLBACK_LIBRARY_PATH = parseColonList(value);
+		sEnv.DYLD_FALLBACK_LIBRARY_PATH = parseColonList(value);
 	}
 	else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) {
-		if ( !disableIfBadUser((char*)value) ) {
-			if ( strcmp(value, "/") != 0 ) {
-				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");
-						sEnv.DYLD_ROOT_PATH = NULL;
-					}
+		if ( strcmp(value, "/") != 0 ) {
+			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\n");
+					sEnv.DYLD_ROOT_PATH = NULL;
+					break;
 				}
 			}
 		}
 	} 
 	else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
-		if ( !disableIfBadUser((char*)value) )
-			gLinkContext.imageSuffix = value;
+		gLinkContext.imageSuffix = value;
 	}
 	else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
-		if ( !disableIfBadUser((char*)value) )
-			sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value);
+		sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value);
 	}
 	else if ( strcmp(key, "DYLD_DEBUG_TRACE") == 0 ) {
 		fprintf(stderr, "dyld: warning DYLD_DEBUG_TRACE not supported\n");
@@ -642,7 +614,50 @@
 	}
 }
 
-static void checkEnvironmentVariables(const char* envp[])
+//
+// For security, setuid programs ignore DYLD_* environment variables.
+// Additionally, the DYLD_* enviroment variables are removed
+// from the environment, so that any child processes don't see them.
+//
+static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
+{
+	// delete all DYLD_* and LD_LIBRARY_PATH environment variables
+	int removedCount = 0;
+	const char** d = envp;
+	for(const char** s = envp; *s != NULL; s++) {
+	    if ( (strncmp(*s, "DYLD_", 5) != 0) && (strncmp(*s, "LD_LIBRARY_PATH=", 16) != 0) ) {
+			*d++ = *s;
+		}
+		else {
+			++removedCount;
+		}
+	}
+	*d++ = NULL;
+	
+	// slide apple parameters
+	if ( removedCount > 0 ) {
+		*applep = d;
+		do {
+			*d = d[removedCount];
+		} while ( *d++ != NULL );
+	}
+	
+	// setup DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment
+	if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) {
+		const char** paths = sFrameworkFallbackPaths;
+		removePathWithPrefix(paths, "$HOME");
+		sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = paths;
+	}
+
+	// default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment
+	if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) {
+		const char** paths = sLibraryFallbackPaths;
+		removePathWithPrefix(paths, "$HOME");
+		sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths;
+	}
+}
+
+static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron)
 {
 	const char* home = NULL;
 	const char** p;
@@ -650,10 +665,10 @@
 		const char* keyEqualsValue = *p;
 	    if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) {
 			const char* equals = strchr(keyEqualsValue, '=');
-			if ( equals != NULL ) {
+			if ( (equals != NULL) && !ignoreEnviron ) {
 				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);
@@ -664,32 +679,27 @@
 		}
 		else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) {
 			const char* path = &keyEqualsValue[16];
-			if ( !disableIfBadUser((char*)path) )
-				sEnv.LD_LIBRARY_PATH = parseColonList(path);
-		}
-	}
-	
+			sEnv.LD_LIBRARY_PATH = parseColonList(path);
+		}
+	}
+		
 	// default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment
 	if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) {
 		const char** paths = sFrameworkFallbackPaths;
-		if ( home != NULL ) {
-			if ( riskyUser() )
-				removePathWithPrefix(paths, "$HOME");
-			else
-				paths_expand_roots(paths, "$HOME", home);
-		}
+		if ( home == NULL )
+			removePathWithPrefix(paths, "$HOME");
+		else
+			paths_expand_roots(paths, "$HOME", home);
 		sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = paths;
 	}
 
 	// default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment
 	if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) {
 		const char** paths = sLibraryFallbackPaths;
-		if ( home != NULL ) {
-			if ( riskyUser() )
-				removePathWithPrefix(paths, "$HOME");
-			else
-				paths_expand_roots(paths, "$HOME", home);
-		}
+		if ( home == NULL ) 
+			removePathWithPrefix(paths, "$HOME");
+		else
+			paths_expand_roots(paths, "$HOME", home);
 		sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths;
 	}
 }
@@ -731,15 +741,36 @@
 
 uint32_t getImageCount()
 {
-	return sAllImages.size();
+	if ( sAllImagesMightContainUnlinkedImages ) {
+		uint32_t count = 0;
+		for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
+			if ( (*it)->isLinked() )
+				++count;
+		}
+		return count;
+	}
+	else {
+		return sAllImages.size();
+	}
 }
 
 ImageLoader* getIndexedImage(unsigned int index)
 {
-	if ( index < sAllImages.size() )
-		return sAllImages[index];
-	else
-		return NULL;
+	if ( sAllImagesMightContainUnlinkedImages ) {
+		uint32_t count = 0;
+		for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
+			if ( (*it)->isLinked() ) {
+				if ( index == count )
+					return *it;
+				++count;
+			}
+		}
+	}
+	else {
+		if ( index < sAllImages.size() )
+			return sAllImages[index];
+	}
+	return NULL;
 }
 
 ImageLoader* findImageByMachHeader(const struct mach_header* target)
@@ -786,6 +817,20 @@
 #if FIND_STATS	
 		++cacheNotMacho;
 #endif
+	return NULL;
+}
+
+ImageLoader* findImageContainingAddressThreadSafe(const void* addr)
+{
+	// do exhastive search 
+	// todo: consider maintaining a list sorted by address ranges and do a binary search on that
+	const unsigned int imageCount = sAllImages.size();
+	for(unsigned int i=0; i < imageCount; ++i) {
+		ImageLoader* anImage = sAllImages[i];
+		if ( anImage->containsAddress(addr) ) {
+			return anImage;
+		}
+	}
 	return NULL;
 }
 
@@ -1009,6 +1054,13 @@
 						return true;
 					}
 					break;
+				case CPU_TYPE_X86_64:
+					if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_X86_64_ALL ) {
+						*offset = OSSwapBigToHostInt32(archs[i].offset);
+						*len = OSSwapBigToHostInt32(archs[i].size);
+						return true;
+					}
+					break;
 			}
 		}
 	}
@@ -1088,6 +1140,10 @@
 					if ( mh->cpusubtype == CPU_SUBTYPE_I386_ALL ) 
 						return true;
 					break;					
+				case CPU_TYPE_X86_64:
+					if ( mh->cpusubtype == CPU_SUBTYPE_X86_64_ALL ) 
+ 						return true;
+					break;		
 			}
 		}
 	}
@@ -1106,7 +1162,7 @@
 		return image;
 	}
 	
-	return NULL;
+	throw "main executable not a known format";
 }
 
 
@@ -1142,7 +1198,7 @@
 			pread(fd, firstPage, 4096, fileOffset);
 		}
 		else {
-			throw "no matching architecture in fat wrapper";
+			throw "no matching architecture in universal wrapper";
 		}
 	}
 	
@@ -1189,8 +1245,18 @@
 	
 	// try other file formats...
 	
-	throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", 
-		firstPage[0], firstPage[1], firstPage[2], firstPage[3], firstPage[4], firstPage[5], firstPage[6],firstPage[7]);
+	
+	// throw error about what was found
+	switch (*(uint32_t*)firstPage) {
+		case MH_MAGIC:
+		case MH_CIGAM:
+		case MH_MAGIC_64:
+		case MH_CIGAM_64:
+			throw "mach-o, but wrong architecture";
+		default:
+		throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", 
+			firstPage[0], firstPage[1], firstPage[2], firstPage[3], firstPage[4], firstPage[5], firstPage[6],firstPage[7]);
+	}
 }
 
 
@@ -1425,8 +1491,8 @@
 	if ( image != NULL )
 		return image;
 	
-	// try fallback paths
-	if ( (sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_FALLBACK_LIBRARY_PATH != NULL) ) {
+	// try fallback paths during second time (will open file)
+	if ( (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_FALLBACK_LIBRARY_PATH != NULL)) ) {
 		image = loadPhase2(path, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, sEnv.DYLD_FALLBACK_LIBRARY_PATH, exceptions);
 		if ( image != NULL )
 			return image;
@@ -1551,6 +1617,20 @@
 
 ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName)
 {
+	// if fat wrapper, find usable sub-file
+	const fat_header* memStartAsFat = (fat_header*)mem;
+	uint64_t fileOffset = 0;
+	uint64_t fileLength = len;
+	if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+		if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) {
+			mem = &mem[fileOffset];
+			len = fileLength;
+		}
+		else {
+			throw "no matching architecture in universal wrapper";
+		}
+	}
+
 	// try mach-o each loader
 	if ( isCompatibleMachO(mem) ) {
 		ImageLoader* image = new ImageLoaderMachO(moduleName, (mach_header*)mem, len, gLinkContext);
@@ -1562,8 +1642,17 @@
 	
 	// try other file formats...
 	
-	throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", 
-		mem[0], mem[1], mem[2], mem[3], mem[4], mem[5], mem[6],mem[7]);
+	// throw error about what was found
+	switch (*(uint32_t*)mem) {
+		case MH_MAGIC:
+		case MH_CIGAM:
+		case MH_MAGIC_64:
+		case MH_CIGAM_64:
+			throw "mach-o, but wrong architecture";
+		default:
+		throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", 
+			mem[0], mem[1], mem[2], mem[3], mem[4], mem[5], mem[6],mem[7]);
+	}
 }
 
 
@@ -1616,7 +1705,7 @@
 	
 #if __ppc__ || __ppc64__
 	__asm__  ("trap");
-#elif __i386__
+#elif __i386__ || __x86_64__
 	__asm__  ("int3");
 #else
 	#error unknown architecture
@@ -1635,8 +1724,17 @@
 #endif
 	// lookup and bind lazy pointer and get target address
 	try {
+		ImageLoader* target;
+	#if __i386__
+		// fast stubs pass NULL for mh and image is instead found via the location of stub (aka lazyPointer)
+		if ( mh == NULL )
+			target = dyld::findImageContainingAddressThreadSafe(lazyPointer);
+		else
+			target = dyld::findImageByMachHeader(mh);
+	#else
 		// note, target should always be mach-o, because only mach-o lazy handler wired up to this
-		ImageLoader* target = dyld::findImageByMachHeader(mh);
+		target = dyld::findImageByMachHeader(mh);
+	#endif
 		if ( target == NULL )
 			throw "image not found for lazy pointer";
 		result = target->doBindLazySymbol(lazyPointer, gLinkContext);
@@ -1819,7 +1917,18 @@
 	gLinkContext.apple					= apple;
 }
 
-
+static bool isRosetta()
+{
+	int mib[] = { CTL_KERN, KERN_CLASSIC, getpid() };
+	int is_classic = 0;
+	size_t len = sizeof(int);
+	int ret = sysctl(mib, 3, &is_classic, &len, NULL, 0);
+	if ((ret != -1) && is_classic) {
+		// we're running under Rosetta 
+		return true;
+	}
+	return false;
+}
 
 void link(ImageLoader* image, ImageLoader::BindingLaziness bindness, ImageLoader::InitializerRunning runInitializers)
 {
@@ -1841,7 +1950,13 @@
 	}
 
 	// process images
-	image->link(gLinkContext, bindness, runInitializers, sAddImageCallbacks.size());
+	try {
+		image->link(gLinkContext, bindness, runInitializers, sAddImageCallbacks.size());
+	}
+	catch (const char* msg) {
+		sAllImagesMightContainUnlinkedImages = true;
+		throw msg;
+	}
 	
 #if OLD_GDB_DYLD_INTERFACE
 	// notify gdb that loaded libraries have changed
@@ -1851,6 +1966,17 @@
 
 
 //
+// _pthread_keys is partitioned in a lower part that dyld will use; libSystem
+// will use the upper part.  We set __pthread_tsd_first to 1 as the start of
+// the lower part.  Libc will take #1 and c++ exceptions will take #2.  There
+// is one free key=3 left.
+//
+extern "C" {
+	extern int __pthread_tsd_first;
+}
+ 
+
+//
 // Entry point for dyld.  The kernel loads dyld and jumps to __dyld_start which
 // sets up some registers and call this function.
 //
@@ -1859,27 +1985,41 @@
 uintptr_t
 _main(const struct mach_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[])
 {	
+	// set pthread keys to dyld range
+	__pthread_tsd_first = 1;
+	
 	// 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];
+	bool ignoreEnvironmentVariables = false;
+#if __i386__
+	if ( isRosetta() ) {
+		// under Rosetta (x86 side)
+		// When a 32-bit ppc program is run under emulation on an Intel processor,
+		// we want any i386 dylibs (e.g. any used by Rosetta) to not load in the shared region
+		// because the shared region is being used by ppc dylibs
+		gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
+		sExecPath = strdup(apple[0] + strlen(apple[0]) + 1);
+		ignoreEnvironmentVariables = true;
+	}
+#endif
+	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;
 		}
 	}
 	uintptr_t result = 0;
 	sMainExecutableMachHeader = mainExecutableMH;
-	checkEnvironmentVariables(envp);
+	if ( issetugid() )
+		pruneEnvironmentVariables(envp, &apple);
+	else
+		checkEnvironmentVariables(envp, ignoreEnvironmentVariables);
 	if ( sEnv.DYLD_PRINT_OPTS ) 
 		printOptions(argv);
 	if ( sEnv.DYLD_PRINT_ENV ) 
@@ -1937,8 +2077,9 @@
 		fprintf(stderr, "dyld: launch failed\n");
 	}
 	
+
 	// Link in any inserted libraries.  
-	// Do this after link main executable so any extra libraries pulled in by inserted libraries are at end of flat namespace
+	// Do this after linking main executable so any extra libraries pulled in by inserted libraries are at end of flat namespace
 	if ( insertLibrariesCount > 0 ) {
 		for (int i=0; i < insertLibrariesCount; ++i) {
 			try {