Loading...
src/ImageLoaderMachO.cpp dyld-46.16 dyld-44.17
--- dyld/dyld-46.16/src/ImageLoaderMachO.cpp
+++ dyld/dyld-44.17/src/ImageLoaderMachO.cpp
@@ -34,12 +34,8 @@
 #include <mach-o/reloc.h> 
 #include <mach-o/nlist.h> 
 #include <sys/sysctl.h>
-#include <libkern/OSAtomic.h>
 #if __ppc__ || __ppc64__
 	#include <mach-o/ppc/reloc.h>
-#endif
-#if __x86_64__
-	#include <mach-o/x86_64/reloc.h>
 #endif
 
 #ifndef S_ATTR_SELF_MODIFYING_CODE
@@ -80,11 +76,6 @@
 	struct macho_routines_command	: public routines_command  {};	
 #endif
 
-#if __x86_64__
-	#define POINTER_RELOC X86_64_RELOC_UNSIGNED
-#else
-	#define POINTER_RELOC GENERIC_RELOC_VANILLA
-#endif
 
 uint32_t ImageLoaderMachO::fgHintedBinaryTreeSearchs = 0;
 uint32_t ImageLoaderMachO::fgUnhintedBinaryTreeSearchs = 0;
@@ -419,8 +410,6 @@
 					}
 					sNextAltLoadAddress += 0x00100000;  // skip ahead 1MB and try again
 					if ( (sNextAltLoadAddress & 0xF0000000) == 0x90000000 )
-						sNextAltLoadAddress = 0xB0000000;
-					if ( (sNextAltLoadAddress & 0xF0000000) == 0xF0000000 )
 						throw "can't map split seg anywhere";
 					foundRoom = false;
 					break;
@@ -1128,9 +1117,6 @@
 			#elif __i386__
 				const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
 				return (void*)registers->eip;
-			#elif __x86_64__
-				const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
-				return (void*)registers->rip;
 			#else
 				#warning need processor specific code
 			#endif
@@ -1206,29 +1192,19 @@
 	return info;
 }
 
-uintptr_t ImageLoaderMachO::getFirstWritableSegmentAddress()
-{
-	// in split segment libraries r_address is offset from first writable segment
-	for (std::vector<class Segment*>::iterator it=fSegments.begin(); it != fSegments.end(); ++it) {
-		if ( (*it)->writeable() ) {
-			return (*it)->getActualLoadAddress();
-		}
-	}
-	throw "no writable segment";
-}
 
 uintptr_t ImageLoaderMachO::getRelocBase()
 {
-#if __x86_64__
-	// r_address is offset from first writable segment
-	return getFirstWritableSegmentAddress();
-#endif
-#if __ppc__ || __i386__
 	if ( fIsSplitSeg ) {
 		// in split segment libraries r_address is offset from first writable segment
-		return getFirstWritableSegmentAddress();
-	}
-#endif
+		const unsigned int segmentCount = fSegments.size();
+		for(unsigned int i=0; i < segmentCount; ++i){
+			Segment* seg = fSegments[i];
+			if ( seg->writeable() ) {
+				return seg->getActualLoadAddress();
+			}
+		}
+	}
 	
 	// in non-split segment libraries r_address is offset from first segment
 	return fSegments[0]->getActualLoadAddress();
@@ -1261,52 +1237,19 @@
 }
 #endif
 
-#if __ppc__ || __i386__
-void ImageLoaderMachO::resetPreboundLazyPointers(const LinkContext& context, uintptr_t relocBase)
-{
-	// loop through all local (internal) relocation records looking for pre-bound-lazy-pointer values
-	register const uintptr_t slide = this->fSlide;
-	const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]);
-	const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel];
-	for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
-		if ( (reloc->r_address & R_SCATTERED) != 0 ) {
-			const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc;
-			if (sreloc->r_length == RELOC_SIZE) {
-				uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase);
-				switch(sreloc->r_type) {
-		#if __ppc__ 
-					case PPC_RELOC_PB_LA_PTR:
-						*locationToFix = sreloc->r_value + slide;
-						break;
-		#endif
-		#if __i386__
-					case GENERIC_RELOC_PB_LA_PTR:
-						*locationToFix = sreloc->r_value + slide;
-						break;
-		#endif
-				}
-			}
-		}
-	}
-}
-#endif
-
 void ImageLoaderMachO::doRebase(const LinkContext& context)
 {
 	// if prebound and loaded at prebound address, then no need to rebase
-	if ( this->usablePrebinding(context) ) {
+	// Note: you might think that the check for allDependentLibrariesAsWhenPreBound() is not needed
+	// but it is.  If a dependent library changed, this image's lazy pointers into that library
+	// need to be updated (reset back to lazy binding handler).  That work is done most easily
+	// here because there is a PPC_RELOC_PB_LA_PTR reloc record for each lazy pointer.
+	if ( this->usablePrebinding(context) && this->usesTwoLevelNameSpace() ) {
 		// skip rebasing cause prebound and prebinding not disabled
 		++fgImagesWithUsedPrebinding; // bump totals for statistics
 		return;
 	}
-	
-	// <rdar://problem/5146059> update_prebinding fails if a prebound dylib depends on a non-prebound dylib
-	// In the unusual case that we find a prebound dylib dependent on un-prebound dylib and we are running
-	// update_prebinding, we want the link of the prebound dylib to fail so that it will be excluded from 
-	// the list of dylibs to be re-written.
-	if ( context.prebinding && !this->isPrebindable() )
-		throwf("dylib not prebound: %s", this->getPath());
-	
+		
 	// print why prebinding was not used
 	if ( context.verbosePrebinding ) {
 		if ( !this->isPrebindable() ) {
@@ -1326,41 +1269,18 @@
 		}
 	}
 
-	// cache values that are used in the following loop
-	const uintptr_t relocBase = this->getRelocBase();
-	register const uintptr_t slide = this->fSlide;
-
-#if __ppc__ || __i386__
-	// if prebound and we got here, then prebinding is not valid, so reset all lazy pointers
-	if ( this->isPrebindable() )
-		this->resetPreboundLazyPointers(context, relocBase);
-#endif
-
-	// if loaded at preferred address, no rebasing necessary
-	if ( slide == 0 ) 
-		return;
-
 	// if there are __TEXT fixups, temporarily make __TEXT writable
 	if ( fTextSegmentWithFixups != NULL ) 
 		fTextSegmentWithFixups->tempWritable();
 
+	// 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]);
 	const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel];
 	for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
-	#if __x86_64__
-		// only one kind of local relocation supported for x86_64
-		if ( reloc->r_length != 3 ) 
-			throw "bad local relocation length";
-		if ( reloc->r_type != X86_64_RELOC_UNSIGNED ) 
-			throw "unknown local relocation type";
-		if ( reloc->r_pcrel != 0 ) 
-			throw "bad local relocation pc_rel";
-		if ( reloc->r_extern != 0 ) 
-			throw "extern relocation found with local relocations";
-		*((uintptr_t*)(reloc->r_address + relocBase)) += slide;
-	#endif
-	#if __ppc__ || __ppc64__ || __i386__
 		if ( (reloc->r_address & R_SCATTERED) == 0 ) {
 			if ( reloc->r_symbolnum == R_ABS ) {
 				// ignore absolute relocations
@@ -1395,6 +1315,12 @@
 					case GENERIC_RELOC_VANILLA:
 						*locationToFix += slide;
 						break;
+		#if __ppc__ || __ppc64__
+					case PPC_RELOC_PB_LA_PTR:
+						// should only see these in prebound images, and we got here so prebinding is being ignored
+						*locationToFix = sreloc->r_value + slide;
+						break;
+		#endif
 		#if __ppc__
 					case PPC_RELOC_HI16: 
 					case PPC_RELOC_LO16: 
@@ -1404,18 +1330,10 @@
 						otherRelocsPPC(locationToFix, sreloc->r_type, reloc->r_address, slide);
 						break;
 		#endif
-		#if __ppc__ 
-					case PPC_RELOC_PB_LA_PTR:
-						// do nothing
-						break;
-		#elif __ppc64__
-					case PPC_RELOC_PB_LA_PTR:
-						// these should never exist in ppc64, but the first ld64 had a bug and created them
+		#if __i386__
+					case GENERIC_RELOC_PB_LA_PTR:
+						// should only see these in prebound images, and we got here so prebinding is being ignored
 						*locationToFix = sreloc->r_value + slide;
-						break;
-		#elif __i386__
-					case GENERIC_RELOC_PB_LA_PTR:
-						// do nothing
 						break;
 		#endif
 					default:
@@ -1426,7 +1344,6 @@
 				throw "bad local scattered relocation length";
 			}
 		}
-	#endif
 	}
 	
 	// if there were __TEXT fixups, restore write protection
@@ -1596,7 +1513,6 @@
 		}
 	}
 
-    
 	return NULL;
 }
 
@@ -1763,12 +1679,6 @@
 
 	if ( context.bindFlat || !twoLevel ) {
 		// flat lookup
-		if ( ((undefinedSymbol->n_type & N_PEXT) != 0) && ((undefinedSymbol->n_type & N_TYPE) == N_SECT) ) {
-			// is a multi-module private_extern internal reference that the linker did not optimize away
-			uintptr_t addr = undefinedSymbol->n_value + this->fSlide;
-			*foundIn = this;
-			return addr;
-		}
 		const Symbol* sym;
 		if ( context.flatExportFinder(symbolName, &sym, foundIn) )
 			return (*foundIn)->getExportedSymbolAddress(sym);
@@ -1779,6 +1689,13 @@
 			if ( sym != NULL )
 				return (*foundIn)->getExportedSymbolAddress(sym);
 		}
+		if ( ((undefinedSymbol->n_type & N_PEXT) != 0) || ((undefinedSymbol->n_type & N_TYPE) == N_SECT) ) {
+			// could be a multi-module private_extern internal reference
+			// the static linker squirrels away the target address in n_value
+			uintptr_t addr = undefinedSymbol->n_value + this->fSlide;
+			*foundIn = this;
+			return addr;
+		}
 		if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) {
 			// definition can't be found anywhere
 			// if reference is weak_import, then it is ok, just return 0
@@ -1788,7 +1705,7 @@
 	}
 	else {
 		// symbol requires searching images with coalesced symbols
-		if ( !context.prebinding && this->needsCoalescing() && symbolRequiresCoalescing(undefinedSymbol) ) {
+		if ( this->needsCoalescing() && symbolRequiresCoalescing(undefinedSymbol) ) {
 			const Symbol* sym;
 			if ( context.coalescedExportFinder(symbolName, &sym, foundIn) )
 				return (*foundIn)->getExportedSymbolAddress(sym);
@@ -1834,7 +1751,7 @@
 		}
 		
 		if ( target == NULL ) {
-			//fprintf(stderr, "resolveUndefined(%s) in %s\n", symbolName, this->getPath());
+			fprintf(stderr, "resolveUndefined(%s) in %s\n", symbolName, this->getPath());
 			throw "symbol not found";
 		}
 		
@@ -1917,7 +1834,7 @@
 	for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
 		if (reloc->r_length == RELOC_SIZE) {
 			switch(reloc->r_type) {
-				case POINTER_RELOC:
+				case GENERIC_RELOC_VANILLA:
 					{
 						const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum];
 						// if only processing coalesced symbols and this one does not require coalesceing, skip to next
@@ -2044,35 +1961,14 @@
 #if __i386__
 	// i386 has special self-modifying stubs that change from "CALL rel32" to "JMP rel32"
 	if ( ((sect->flags & SECTION_TYPE) == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) {
+		uint8_t* const jmpTableEntryToPatch = (uint8_t*)ptrToBind;
 		uint32_t rel32 = targetAddr - (((uint32_t)ptrToBind)+5);
-		// re-write instruction in a thread-safe manner
-		// use 8-byte compare-and-swap to alter 5-byte jump table entries
-		// loop is required in case the extra three bytes that cover the next entry are altered by another thread
-		bool done = false;
-		while ( !done ) {
-			volatile int64_t* jumpPtr = (int64_t*)ptrToBind;
-			int pad = 0;
-			// By default the three extra bytes swapped follow the 5-byte JMP.
-			// But, if the 5-byte jump is up against the end of the __IMPORT segment
-			// We don't want to access bytes off the end of the segment, so we shift
-			// the extra bytes to precede the 5-byte JMP.
-			if ( (((uint32_t)ptrToBind + 8) & 0x00000FFC) == 0x00000000 ) {
-				jumpPtr = (int64_t*)((uint32_t)ptrToBind - 3);
-				pad = 3;
-			}
-			int64_t oldEntry = *jumpPtr;
-			union {
-				int64_t int64;
-				uint8_t bytes[8];
-			} newEntry;
-			newEntry.int64 = oldEntry;
-			newEntry.bytes[pad+0] = 0xE9; // JMP rel32
-			newEntry.bytes[pad+1] = rel32 & 0xFF;
-			newEntry.bytes[pad+2] = (rel32 >> 8) & 0xFF;
-			newEntry.bytes[pad+3] = (rel32 >> 16) & 0xFF;
-			newEntry.bytes[pad+4] = (rel32 >> 24) & 0xFF;
-			done = OSAtomicCompareAndSwap64Barrier(oldEntry, newEntry.int64, (int64_t*)jumpPtr);
-		}
+		//fprintf(stderr, "rewriting stub at %p\n", jmpTableEntryToPatch);
+		jmpTableEntryToPatch[0] = 0xE9; // JMP rel32
+		jmpTableEntryToPatch[1] = rel32 & 0xFF;
+		jmpTableEntryToPatch[2] = (rel32 >> 8) & 0xFF;
+		jmpTableEntryToPatch[3] = (rel32 >> 16) & 0xFF;
+		jmpTableEntryToPatch[4] = (rel32 >> 24) & 0xFF;
 	}
 	else
 #endif
@@ -2312,49 +2208,16 @@
 						const uint8_t type = sect->flags & SECTION_TYPE;
 						if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) {
 							// reset each jmp entry in this section
-							const uint32_t indirectTableOffset = sect->reserved1;
-							const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff];
 							uint8_t* start = (uint8_t*)(sect->addr + this->fSlide);
 							uint8_t* end = start + sect->size;
 							uintptr_t dyldHandler = (uintptr_t)&fast_stub_binding_helper_interface;
-							uint32_t entryIndex = 0;
-							for (uint8_t* entry = start; entry < end; entry += 5, ++entryIndex) {
-								bool installLazyHandler = true;
-								// jump table entries that cross a (64-byte) cache line boundary have the potential to cause crashes
-								// if the instruction is updated by one thread while being executed by another
-								if ( ((uint32_t)entry & 0xFFFFFFC0) != ((uint32_t)entry+4 & 0xFFFFFFC0) ) {
-									// need to bind this now to avoid a potential problem if bound lazily
-									uint32_t symbolIndex = indirectTable[indirectTableOffset + entryIndex];
-									// the latest linker marks 64-byte crossing stubs with INDIRECT_SYMBOL_ABS so they are not used
-									if ( symbolIndex != INDIRECT_SYMBOL_ABS ) {
-										const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx];
-										ImageLoader* image = NULL;
-										try {
-											uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), &image);
-											symbolAddr = this->bindIndirectSymbol((uintptr_t*)entry, sect, symbolName, symbolAddr, image, context);
-											++fgTotalBindFixups;
-											uint32_t rel32 = symbolAddr - (((uint32_t)entry)+5);
-											entry[0] = 0xE9; // JMP rel32
-											entry[1] = rel32 & 0xFF;
-											entry[2] = (rel32 >> 8) & 0xFF;
-											entry[3] = (rel32 >> 16) & 0xFF;
-											entry[4] = (rel32 >> 24) & 0xFF;
-											installLazyHandler = false;
-										} 
-										catch (const char* msg) {
-											// ignore errors when binding symbols early
-											// maybe the function is never called, and therefore erroring out now would be a regression
-										}
-									}
-								}
-								if ( installLazyHandler ) {
-									uint32_t rel32 = dyldHandler - (((uint32_t)entry)+5);
-									entry[0] = 0xE8; // CALL rel32
-									entry[1] = rel32 & 0xFF;
-									entry[2] = (rel32 >> 8) & 0xFF;
-									entry[3] = (rel32 >> 16) & 0xFF;
-									entry[4] = (rel32 >> 24) & 0xFF;
-								}
+							for (uint8_t* entry = start; entry < end; entry += 5) {
+								uint32_t rel32 = dyldHandler - (((uint32_t)entry)+5);
+								entry[0] = 0xE8; // CALL rel32
+								entry[1] = rel32 & 0xFF;
+								entry[2] = (rel32 >> 8) & 0xFF;
+								entry[3] = (rel32 >> 16) & 0xFF;
+								entry[4] = (rel32 >> 24) & 0xFF;
 							}
 						}
 					}
@@ -2369,10 +2232,7 @@
 bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const
 {
 	// if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
-	if ( this->isPrebindable() 
-		&& (this->getSlide() == 0) 
-		&& (this->usesTwoLevelNameSpace() || context.prebinding)
-		&& this->allDependentLibrariesAsWhenPreBound() ) {
+	if ( this->isPrebindable() && this->allDependentLibrariesAsWhenPreBound() && (this->getSlide() == 0) ) {
 		// allow environment variables to disable prebinding
 		if ( context.bindFlat )
 			return false;
@@ -2551,22 +2411,16 @@
 					for (const DependentLibrary* dl=fLibraries; dl < &fLibraries[fLibrariesCount]; dl++) {
 						if (strcmp(dl->name, name) == 0 ) {
 							// found matching DependentLibrary for this load command
-							if ( dl->image == NULL ) {
-								// missing weak linked dylib
-								dylib->dylib.timestamp = 0;
+							ImageLoaderMachO* targetImage = (ImageLoaderMachO*)(dl->image); // !!! assume only mach-o images are prebound
+							if ( ! targetImage->isPrebindable() )
+								throw "dependent dylib is not prebound";
+							// if the target is currently being re-prebound then its timestamp will be the same as this one
+							if ( ! targetImage->usablePrebinding(context) ) {
+								dylib->dylib.timestamp = timestamp;
 							}
 							else {
-								ImageLoaderMachO* targetImage = (ImageLoaderMachO*)(dl->image); // !!! assume only mach-o images are prebound
-								if ( ! targetImage->isPrebindable() )
-									throw "dependent dylib is not prebound";
-								// if the target is currently being re-prebound then its timestamp will be the same as this one
-								if ( ! targetImage->usablePrebinding(context) ) {
-									dylib->dylib.timestamp = timestamp;
-								}
-								else {
-									// otherwise dependent library is already correctly prebound, so use its checksum
-									dylib->dylib.timestamp = targetImage->doGetLibraryInfo().checksum;
-								}
+								// otherwise dependent library is already correctly prebound, so use its checksum
+								dylib->dylib.timestamp = targetImage->doGetLibraryInfo().checksum;
 							}
 							break;
 						}
@@ -2664,7 +2518,7 @@
 				switch(sreloc->r_type) {
 		#if __ppc__ || __ppc64__
 					case PPC_RELOC_PB_LA_PTR:
-		#elif __i386__ || __x86_64__
+		#elif __i386__
 					case GENERIC_RELOC_PB_LA_PTR:
 		#else
 			#error unknown architecture