Loading...
src/ImageLoaderMachO.cpp dyld-44.4 dyld-44.17
--- dyld/dyld-44.4/src/ImageLoaderMachO.cpp
+++ dyld/dyld-44.17/src/ImageLoaderMachO.cpp
@@ -38,6 +38,10 @@
 	#include <mach-o/ppc/reloc.h>
 #endif
 
+#ifndef S_ATTR_SELF_MODIFYING_CODE
+  #define S_ATTR_SELF_MODIFYING_CODE 0x04000000
+#endif
+
 #include "ImageLoaderMachO.h"
 #include "mach-o/dyld_gdb.h"
 
@@ -75,6 +79,7 @@
 
 uint32_t ImageLoaderMachO::fgHintedBinaryTreeSearchs = 0;
 uint32_t ImageLoaderMachO::fgUnhintedBinaryTreeSearchs = 0;
+uint32_t ImageLoaderMachO::fgCountOfImagesWithWeakExports = 0;
 
 
 //#define LINKEDIT_USAGE_DEBUG 1
@@ -176,6 +181,12 @@
 	this->parseLoadCmds();
 }
 
+ImageLoaderMachO::~ImageLoaderMachO()
+{
+	// keep count of images with weak exports
+	if ( this->hasCoalescedExports() )
+		--fgCountOfImagesWithWeakExports;
+}
 
 
 
@@ -188,7 +199,8 @@
 	const struct load_command* cmd = cmds;
 	for (unsigned long i = 0; i < cmd_count; ++i) {
 		if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
-			fSegments.push_back(new SegmentMachO((struct macho_segment_command*)cmd, this, fileData));
+			if ( (((struct macho_segment_command*)cmd)->vmsize != 0) || !fIsSplitSeg )
+				fSegments.push_back(new SegmentMachO((struct macho_segment_command*)cmd, this, fileData));
 		}
 		cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
 	}
@@ -894,6 +906,10 @@
 			fMachOData = (uint8_t*)(seg->getActualLoadAddress());
 		}
 	}
+
+	// keep count of prebound images with weak exports
+	if ( this->hasCoalescedExports() )
+		++fgCountOfImagesWithWeakExports;
 
 	// walk load commands (mapped in at start of __TEXT segment)
 	const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
@@ -1926,6 +1942,40 @@
 	return (const void*)seg->getActualLoadAddress();
 }
 
+uintptr_t ImageLoaderMachO::bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, ImageLoader* targetImage, const LinkContext& context)
+{
+	if ( context.verboseBind ) {
+		const char* path = NULL;
+		if ( targetImage != NULL )
+			path = targetImage->getShortName();
+		fprintf(stderr, "dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n",
+				this->getShortName(), symbolName, (((sect->flags & SECTION_TYPE)==S_NON_LAZY_SYMBOL_POINTERS) ? "non_lazy_ptr" : "lazy_ptr"),
+				path, symbolName, (uintptr_t)ptrToBind, targetAddr);
+	}
+	if ( context.bindingHandler != NULL ) {
+		const char* path = NULL;
+		if ( targetImage != NULL )
+			path = targetImage->getShortName();
+		targetAddr = (uintptr_t)context.bindingHandler(path, symbolName, (void *)targetAddr);
+	}
+#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);
+		//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
+	*ptrToBind = targetAddr;
+	return targetAddr;
+}
+
 
 uintptr_t ImageLoaderMachO::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context)
 {
@@ -1944,38 +1994,39 @@
 					const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
 					for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
 						const uint8_t type = sect->flags & SECTION_TYPE;
+						uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL;
 						if ( type == S_LAZY_SYMBOL_POINTERS ) {
 							const uint32_t pointerCount = sect->size / sizeof(uintptr_t);
 							uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide);
 							if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) {
 								const uint32_t indirectTableOffset = sect->reserved1;
 								const uint32_t lazyIndex = lazyPointer - symbolPointers;
-								uint32_t symbolIndex = indirectTable[indirectTableOffset + lazyIndex];
-								if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) {
-									ImageLoader *image = NULL;
-									const char *path = NULL;
-									uintptr_t symbolAddr = this->resolveUndefined(context,  &fSymbolTable[symbolIndex], twoLevel, &image);
-									if ( context.verboseBind ) {
-										if(NULL == path && NULL != image) {
-											path = image->getShortName();
-										}
-										fprintf(stderr, "dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n",
-												this->getShortName(), &fStrings[fSymbolTable[symbolIndex].n_un.n_strx], "lazy_ptr",
-												path, &fStrings[fSymbolTable[symbolIndex].n_un.n_strx], (uintptr_t)&symbolPointers[lazyIndex], symbolAddr);
-									}
-									if ( NULL != context.bindingHandler ) {
-										if(NULL == path && NULL != image) {
-											path = image->getPath();
-										}
-										symbolAddr = (uintptr_t)context.bindingHandler(path, &fStrings[fSymbolTable[symbolIndex].n_un.n_strx], (void *)symbolAddr);
-									}
-									symbolPointers[lazyIndex] = symbolAddr;
-									// update stats
-									fgTotalLazyBindFixups++;
-									return symbolPointers[lazyIndex];
-								}
+								symbolIndex = indirectTable[indirectTableOffset + lazyIndex];
 							}
 						}
+					#if __i386__
+						else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) {
+							// 5 bytes stubs on i386 are new "fast stubs"
+							uint8_t* const jmpTableBase = (uint8_t*)(sect->addr + fSlide);
+							uint8_t* const jmpTableEnd = jmpTableBase + sect->size;
+							// initial CALL instruction in jump table leaves pointer to next entry, so back up
+							uint8_t* const jmpTableEntryToPatch = ((uint8_t*)lazyPointer) - 5;  
+							lazyPointer = (uintptr_t*)jmpTableEntryToPatch; 
+							if ( (jmpTableEntryToPatch >= jmpTableBase) && (jmpTableEntryToPatch < jmpTableEnd) ) {
+								const uint32_t indirectTableOffset = sect->reserved1;
+								const uint32_t entryIndex = (jmpTableEntryToPatch - jmpTableBase)/5;
+								symbolIndex = indirectTable[indirectTableOffset + entryIndex];
+							}
+						}
+					#endif
+						if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) {
+							const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx];
+							ImageLoader* image = NULL;
+							uintptr_t symbolAddr = this->resolveUndefined(context,  &fSymbolTable[symbolIndex], twoLevel, &image);
+							symbolAddr = this->bindIndirectSymbol(lazyPointer, sect, symbolName, symbolAddr, image,  context);
+							++fgTotalLazyBindFixups;
+							return symbolAddr;
+						}
 					}
 				}
 				break;
@@ -1984,6 +2035,7 @@
 	}
 	throw "lazy pointer not found";
 }
+
 
 
 
@@ -2004,26 +2056,37 @@
 					const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
 					for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
 						const uint8_t type = sect->flags & SECTION_TYPE;
-						const uint32_t pointerCount = sect->size / sizeof(uintptr_t);
+						uint32_t elementSize = sizeof(uintptr_t);
+						uint32_t elementCount = sect->size / elementSize;
 						if ( type == S_NON_LAZY_SYMBOL_POINTERS ) {
 							if ( (bindness == kLazyOnly) || (bindness == kLazyOnlyNoDependents) )
 								continue;
 						}
 						else if ( type == S_LAZY_SYMBOL_POINTERS ) {
 							// process each symbol pointer in this section
-							fgTotalPossibleLazyBindFixups += pointerCount;
+							fgTotalPossibleLazyBindFixups += elementCount;
 							if ( bindness == kNonLazyOnly )
 								continue;
 						}
+				#if __i386__
+						else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) {
+							// process each jmp entry in this section
+							elementCount = sect->size / 5;
+							elementSize = 5;
+							fgTotalPossibleLazyBindFixups += elementCount;
+							if ( bindness == kNonLazyOnly )
+								continue;
+						}
+				#endif
 						else {
 							continue;
 						}
 						const uint32_t indirectTableOffset = sect->reserved1;
-						uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide);
-						for (uint32_t j=0; j < pointerCount; ++j) {
+						uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide);
+						for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) {
 							uint32_t symbolIndex = indirectTable[indirectTableOffset + j];
 							if ( symbolIndex == INDIRECT_SYMBOL_LOCAL) {
-								symbolPointers[j] += this->fSlide;
+								*((uintptr_t*)ptrToBind) += this->fSlide;
 							}
 							else if ( symbolIndex == INDIRECT_SYMBOL_ABS) {
 								// do nothing since already has absolute address
@@ -2061,27 +2124,13 @@
 									continue;
 								uintptr_t symbolAddr;
 									symbolAddr = resolveUndefined(context, sym, twoLevel, &image);
-								if ( context.verboseBind ) {
-									const char *path = NULL;
-									if(NULL != image) {
-										path = image->getShortName();
-									}
-									const char *typeName;
-									if ( type == S_LAZY_SYMBOL_POINTERS ) {
-										typeName = "lazy_ptr";
-									}
-									else {
-										typeName = "non_lazy_ptr";
-									}
-									fprintf(stderr, "dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n",
-											this->getShortName(), &fStrings[sym->n_un.n_strx], typeName,
-											path, &fStrings[sym->n_un.n_strx], (uintptr_t)&symbolPointers[j], symbolAddr);
-								}
-								symbolPointers[j] = symbolAddr;
+									
+								// update pointer
+								symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image,  context);
 							}
 						}
 						// update stats
-						fgTotalBindFixups += pointerCount;
+						fgTotalBindFixups += elementCount;
 					}
 				}
 				break;
@@ -2120,9 +2169,10 @@
 // These are defined in dyldStartup.s
 extern "C" void stub_binding_helper();
 extern "C" bool dyld_func_lookup(const char* name, uintptr_t* address);
-
-
-void ImageLoaderMachO::setupLazyPointerHandler()
+extern "C" void fast_stub_binding_helper_interface();
+
+
+void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context)
 {
 	if ( fDATAdyld != NULL ) {
 		struct DATAdyld* dd = (struct DATAdyld*)(fDATAdyld->addr + fSlide);
@@ -2141,6 +2191,42 @@
 		//	save = dd->stubBindHelper;	
 #endif
 	}
+#if __i386__
+	if ( ! this->usablePrebinding(context) || !this->usesTwoLevelNameSpace() ) {
+		// reset all "fast" stubs
+		const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
+		const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+		const struct load_command* cmd = cmds;
+		for (uint32_t i = 0; i < cmd_count; ++i) {
+			switch (cmd->cmd) {
+				case LC_SEGMENT_COMMAND:
+				{
+					const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+					const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
+					const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
+					for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+						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
+							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;
+							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;
+							}
+						}
+					}
+				}
+			}
+			cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+		}
+	}
+#endif
 }
 
 bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const
@@ -2167,13 +2253,13 @@
 void ImageLoaderMachO::doBind(const LinkContext& context, BindingLaziness bindness)
 {
 	// set dyld entry points in image
-	this->setupLazyPointerHandler();
+	this->setupLazyPointerHandler(context);
 
 	// if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
 	// note: flat-namespace binaries need to be imports rebound (even if correctly prebound)
 	if ( this->usablePrebinding(context) && this->usesTwoLevelNameSpace() ) {
-		// if image has coalesced symbols, then these need to be rebound
-		if ( this->needsCoalescing() ) {
+		// if image has coalesced symbols, then these need to be rebound, unless this is the only image with weak symbols
+		if ( this->needsCoalescing() && (fgCountOfImagesWithWeakExports > 1) ) {
 			this->doBindExternalRelocations(context, true);
 			this->doBindIndirectSymbolPointers(context, kLazyAndNonLazy, true);
 		}
@@ -2278,6 +2364,7 @@
 	ImageLoader::printStatistics(imageCount);
 	fprintf(stderr, "total hinted binary tree searches:    %d\n", fgHintedBinaryTreeSearchs);
 	fprintf(stderr, "total unhinted binary tree searches:  %d\n", fgUnhintedBinaryTreeSearchs);
+	fprintf(stderr, "total images with weak exports:  %d\n", fgCountOfImagesWithWeakExports);
 	
 #if LINKEDIT_USAGE_DEBUG
 	fprintf(stderr, "linkedit pages accessed (%lu):\n", sLinkEditPageBuckets.size());