]> git.saurik.com Git - apple/dyld.git/commitdiff
dyld-360.14.tar.gz os-x-1011 os-x-10111 v360.14 v360.17
authorApple <opensource@apple.com>
Fri, 4 Sep 2015 17:27:27 +0000 (17:27 +0000)
committerApple <opensource@apple.com>
Fri, 4 Sep 2015 17:27:27 +0000 (17:27 +0000)
57 files changed:
base.xcconfig [deleted file]
configs/base.xcconfig [new file with mode: 0644]
configs/dyld.xcconfig [new file with mode: 0644]
configs/libdyld.xcconfig [new file with mode: 0644]
configs/update_dyld_shared_cache.xcconfig [new file with mode: 0644]
dyld.xcconfig [deleted file]
dyld.xcodeproj/project.pbxproj
include/mach-o/dyld_priv.h
include/objc-shared-cache.h
launch-cache/CacheFileAbstraction.hpp
launch-cache/MachOBinder.hpp
launch-cache/MachOFileAbstraction.hpp
launch-cache/MachOLayout.hpp
launch-cache/MachORebaser.hpp
launch-cache/ObjCLegacyAbstraction.hpp
launch-cache/ObjCModernAbstraction.hpp
launch-cache/dsc_extractor.cpp
launch-cache/dsc_iterator.cpp
launch-cache/dyld_cache_format.h
launch-cache/update_dyld_shared_cache.cpp
launch-cache/update_dyld_shared_cache_entitlements.plist [new file with mode: 0644]
libdyld.xcconfig [deleted file]
src/ImageLoader.cpp
src/ImageLoader.h
src/ImageLoaderMachO.cpp
src/ImageLoaderMachO.h
src/ImageLoaderMachOClassic.cpp
src/ImageLoaderMachOClassic.h
src/ImageLoaderMachOCompressed.cpp
src/ImageLoaderMachOCompressed.h
src/dyld.cpp
src/dyld.h
src/dyldAPIs.cpp
src/dyldAPIsInLibSystem.cpp
src/dyldNew.cpp
src/dyldStartup.s
src/dyldSyscallInterface.h
src/dyld_stub_binder.s
src/glue.c
src/libdyld_data_symbols.dirty [new file with mode: 0644]
src/stub_binding_helper.s
src/threadLocalHelpers.s
src/threadLocalVariables.c
unit-tests/build-iPhoneOS-unit-tests
unit-tests/include/common.makefile
unit-tests/run-all-unit-tests
unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c
unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c
unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c
unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c
unit-tests/test-cases/image_header_containing_address/Makefile [new file with mode: 0644]
unit-tests/test-cases/image_header_containing_address/foo.c [new file with mode: 0644]
unit-tests/test-cases/image_header_containing_address/main.c [new file with mode: 0644]
unit-tests/test-cases/interpose-not-inserted/Makefile [new file with mode: 0644]
unit-tests/test-cases/interpose-not-inserted/main.c [new file with mode: 0644]
unit-tests/test-cases/interpose-not-inserted/mystrdup.c [new file with mode: 0644]
unit-tests/test-cases/interpose-not-inserted/wrap.c [new file with mode: 0644]

diff --git a/base.xcconfig b/base.xcconfig
deleted file mode 100644 (file)
index 91782c7..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#include "<DEVELOPER_DIR>/AppleInternal/XcodeConfig/SimulatorSupport.xcconfig"
-
-// Set INSTALL_PATH[sdk=macosx*] when SimulatorSupport.xcconfig is unavailable
-INSTALL_PATH[sdk=macosx*] = $(INSTALL_PATH_ACTUAL)
diff --git a/configs/base.xcconfig b/configs/base.xcconfig
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/configs/dyld.xcconfig b/configs/dyld.xcconfig
new file mode 100644 (file)
index 0000000..fc7bcdc
--- /dev/null
@@ -0,0 +1,20 @@
+ALIGNMENT[arch=armv7s]     = -Wl,-segalign,0x4000
+
+BASE_ADDRESS[arch=armv7*]  =     0x1fe00000
+BASE_ADDRESS[arch=arm64]   =    0x120000000
+BASE_ADDRESS[arch=i386]    =     0x8fe00000
+BASE_ADDRESS[arch=x86_64]  = 0x7fff5fc00000
+
+ENTRY[sdk=*simulator*]     = -Wl,-e,_start_sim
+ENTRY[sdk=iphoneos*]       = -Wl,-e,__dyld_start
+ENTRY[sdk=macosx*]         = -Wl,-e,__dyld_start
+
+EXPORTED_SYMBOLS_FILE[sdk=*simulator*]   = $(SRCROOT)/src/dyld_sim.exp
+EXPORTED_SYMBOLS_FILE[sdk=iphoneos*]     = $(SRCROOT)/src/dyld.exp
+EXPORTED_SYMBOLS_FILE[sdk=macosx*]       = $(SRCROOT)/src/dyld.exp
+
+PRODUCT_NAME[sdk=*simulator*] = dyld_sim
+PRODUCT_NAME[sdk=iphoneos*]   = dyld
+PRODUCT_NAME[sdk=macosx*]     = dyld
+
+INSTALL_PATH   = /usr/lib
diff --git a/configs/libdyld.xcconfig b/configs/libdyld.xcconfig
new file mode 100644 (file)
index 0000000..756db3d
--- /dev/null
@@ -0,0 +1,8 @@
+
+LIBSYSTEM_LIBS[sdk=*simulator*]     = -Wl,-upward-lsystem_sim_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_sim_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_sim_kernel
+LIBSYSTEM_LIBS[sdk=iphoneos*]       = -Wl,-upward-lsystem_platform     -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread     -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel
+LIBSYSTEM_LIBS[sdk=macosx*]         = -Wl,-upward-lsystem_platform     -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread     -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel
+
+INSTALL_PATH = /usr/lib/system
+
+
diff --git a/configs/update_dyld_shared_cache.xcconfig b/configs/update_dyld_shared_cache.xcconfig
new file mode 100644 (file)
index 0000000..1bed864
--- /dev/null
@@ -0,0 +1,18 @@
+
+//:configuration = Release
+LOCAL_YES = local
+
+//:configuration = Release
+LOCAL = $(LOCAL_$(RC_PURPLE))
+
+//:configuration = Release
+INSTALL_PATH = $(INSTALL_LOCATION)/usr/$(LOCAL)/bin
+
+// don't iOS tool
+MY_RELEASE_CODE_SIGN_IDENTITY_YES =
+MY_RELEASE_CODE_SIGN_IDENTITY_ = -
+CODE_SIGN_IDENTITY = $(MY_RELEASE_CODE_SIGN_IDENTITY_$(RC_PURPLE))
+
+
+CODE_SIGN_ENTITLEMENTS = launch-cache/update_dyld_shared_cache_entitlements.plist
+
diff --git a/dyld.xcconfig b/dyld.xcconfig
deleted file mode 100644 (file)
index 7c6bbfe..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-ALIGNMENT[arch=armv7s]     = -Wl,-segalign,0x4000
-
-BASE_ADDRESS[arch=armv7*]  =     0x1fe00000
-BASE_ADDRESS[arch=arm64]   =    0x120000000
-BASE_ADDRESS[arch=i386]    =     0x8fe00000
-BASE_ADDRESS[arch=x86_64]  = 0x7fff5fc00000
-
-ENTRY[sdk=*simulator*]     = -Wl,-e,_start_sim
-ENTRY[sdk=iphoneos*]       = -Wl,-e,__dyld_start
-ENTRY[sdk=macosx*]         = -Wl,-e,__dyld_start
-
-EXPORTED_SYMBOLS_FILE[sdk=*simulator*]   = $(SRCROOT)/src/dyld_sim.exp
-EXPORTED_SYMBOLS_FILE[sdk=iphoneos*]     = $(SRCROOT)/src/dyld.exp
-EXPORTED_SYMBOLS_FILE[sdk=macosx*]       = $(SRCROOT)/src/dyld.exp
-
-PRODUCT_NAME[sdk=*simulator*] = dyld_sim
-PRODUCT_NAME[sdk=iphoneos*]   = dyld
-PRODUCT_NAME[sdk=macosx*]     = dyld
-
-INSTALL_PATH_ACTUAL   = /usr/lib
index c0dde471d02c57afb7b8f7748a383f80e3b40fae..e17ce86853a72a8761d940932a3db6ffefc100a6 100644 (file)
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
-               3FC074DF188330C8005F11DD /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = "<group>"; };
                834A90AB0E1D85D600555761 /* ObjCLegacyAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCLegacyAbstraction.hpp; sourceTree = "<group>"; };
                834A90AC0E1D85D600555761 /* ObjCModernAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCModernAbstraction.hpp; sourceTree = "<group>"; };
                EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; };
                EF799FEE070D27BB00F78484 /* dlopen.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlopen.3; path = doc/man/man3/dlopen.3; sourceTree = SOURCE_ROOT; };
                EF799FEF070D27BB00F78484 /* dlsym.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlsym.3; path = doc/man/man3/dlsym.3; sourceTree = SOURCE_ROOT; };
                EF799FF0070D27BB00F78484 /* dyld.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dyld.3; path = doc/man/man3/dyld.3; sourceTree = SOURCE_ROOT; };
+               F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = update_dyld_shared_cache_entitlements.plist; sourceTree = "<group>"; };
                F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = "<group>"; };
                F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = "<group>"; };
                F93666DF163B4C42002ECADA /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
                F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = "<group>"; };
                F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = "<group>"; };
                F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = "<group>"; };
+               F971DD131A4A0E0700BBDD52 /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = "<group>"; };
+               F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = "<group>"; };
+               F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = "<group>"; };
+               F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = "<group>"; };
                F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = "<group>"; };
-               F97988FA187F706600EC2C8E /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = "<group>"; };
-               F97988FB187F707A00EC2C8E /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = "<group>"; };
                F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = "<group>"; };
                F98935B90A9A412B00FB6228 /* MachOBinder.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOBinder.hpp; sourceTree = "<group>"; };
                F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachORebaser.hpp; sourceTree = "<group>"; };
                F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = "<group>"; };
                F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = "<group>"; };
                F99B8E670FEC121100701838 /* dyld_shared_cache_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_util; sourceTree = BUILT_PRODUCTS_DIR; };
+               F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = libdyld_data_symbols.dirty; path = src/libdyld_data_symbols.dirty; sourceTree = "<group>"; };
                F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = "<group>"; };
                F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = dyld_stub_binder.s; path = src/dyld_stub_binder.s; sourceTree = "<group>"; };
                F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = "<group>"; };
                F9ED4C980630A76000DF4E74 /* dyld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld; sourceTree = BUILT_PRODUCTS_DIR; };
                F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdyld.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_gdb.cpp; path = src/dyld_gdb.cpp; sourceTree = SOURCE_ROOT; };
-               F9ED4CC70630A7F100DF4E74 /* dyld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; };
+               F9ED4CC70630A7F100DF4E74 /* dyld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
                F9ED4CC80630A7F100DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = src/dyld.h; sourceTree = SOURCE_ROOT; };
                F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIs.cpp; path = src/dyldAPIs.cpp; sourceTree = SOURCE_ROOT; };
                F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyldExceptions.c; path = src/dyldExceptions.c; sourceTree = SOURCE_ROOT; };
                F939373D0A94FC4700070A07 /* launch-cache */ = {
                        isa = PBXGroup;
                        children = (
+                               F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */,
                                F93666E1163B4C58002ECADA /* Security.framework */,
                                F93666DF163B4C42002ECADA /* CoreFoundation.framework */,
                                F939373E0A94FC4700070A07 /* Architectures.hpp */,
                        path = "launch-cache";
                        sourceTree = "<group>";
                };
+               F971DD121A4A0E0700BBDD52 /* configs */ = {
+                       isa = PBXGroup;
+                       children = (
+                               F971DD131A4A0E0700BBDD52 /* base.xcconfig */,
+                               F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */,
+                               F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */,
+                               F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */,
+                       );
+                       path = configs;
+                       sourceTree = "<group>";
+               };
                F9ED4C870630A72200DF4E74 = {
                        isa = PBXGroup;
                        children = (
-                               3FC074DF188330C8005F11DD /* base.xcconfig */,
-                               F97988FA187F706600EC2C8E /* dyld.xcconfig */,
-                               F97988FB187F707A00EC2C8E /* libdyld.xcconfig */,
+                               F971DD121A4A0E0700BBDD52 /* configs */,
                                F9ED4CBB0630A7AA00DF4E74 /* src */,
                                F9ED4CC30630A7BE00DF4E74 /* doc */,
                                F9ED4CBE0630A7B100DF4E74 /* include */,
                                F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */,
                                F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */,
                                F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */,
+                               F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */,
                                F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */,
                                F9B01E3D0739ABDE00CF981B /* dyld.exp */,
                                F976F548127B90F8004BA2A5 /* dyld.order */,
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "if [ \"${PLATFORM_NAME}\" = \"iphoneos\" ] \nthen\n\tmkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n\techo \"existence of this file enables dyld to have dylibs override shared cache\" > ${DSTROOT}//System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache\nfi\n";
+                       shellScript = "if [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n\tmkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n\techo \"existence of this file enables dyld to have dylibs override shared cache\" > ${DSTROOT}//System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache\nfi\n";
                        showEnvVarsInLog = 0;
                };
                F91083C91702592700831889 /* create dyld_cache_config.h */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/bash;
-                       shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -n \"${ARM_SDK}\" ]; then\n\techo -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tawk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\techo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n\techo -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tawk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\techo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n\techo -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tawk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\techo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n\techo -n \"#define ARM64_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tawk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\techo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n\tif [ -z ${RC_PURPLE} ]; then \n\t\techo \"#define ARM_SHARED_REGION_START    0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM_SHARED_REGION_SIZE     0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM64_SHARED_REGION_START 0x180000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM64_SHARED_REGION_SIZE   0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tfi\nfi\n\n";
+                       shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -n \"${ARM_SDK}\" ]; then\n    if [ -r \"${ARM_SDK}/usr/include/mach/shared_region.h\" ]; then\n        echo -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n        echo -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n        echo -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n        echo -n \"#define ARM64_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    else\n        echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${ARM_SDK}/usr/include/mach/shared_region.h'\"\n        exit 1\n    fi\nelse\n\tif [ -z ${RC_PURPLE} ]; then \n\t\techo \"#define ARM_SHARED_REGION_START    0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM_SHARED_REGION_SIZE     0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM64_SHARED_REGION_START 0x180000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM64_SHARED_REGION_SIZE   0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    else\n        echo \"ERROR: env var ARM_SDK not defined\"\n        exit 1\n    fi\nfi\n\n";
                        showEnvVarsInLog = 0;
                };
                F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "if [ \"${PLATFORM_NAME}\" = \"iphonesimulator\" ]\nthen\n    /usr/bin/codesign --force --sign - --entitlements ${SRCROOT}/dyld_sim-entitlements.plist ${INSTALL_DIR}/dyld_sim\nfi\n";
+                       shellScript = "if [ \"${PRODUCT_NAME}\" = \"dyld_sim\" ]\nthen\n    /usr/bin/codesign --force --sign - --entitlements ${SRCROOT}/dyld_sim-entitlements.plist ${INSTALL_DIR}/dyld_sim\nfi\n";
                        showEnvVarsInLog = 0;
                };
                F959621018849DF20003E4D4 /* add dyld symlink */ = {
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 1;
-                       shellPath = /bin/sh;
-                       shellScript = "if [ \"${PLATFORM_NAME}\" = \"iphonesimulator\" ]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
+                       shellPath = /bin/bash;
+                       shellScript = "if [[ \"${PLATFORM_NAME}\"  == *simulator* ]]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
                        showEnvVarsInLog = 0;
                };
                F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n     # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n     # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\nfi\n\nif [ -n \"${RC_PURPLE}\" ]\nthen\t\n    mkdir -p \"${DSTROOT}${INSTALL_PATH_PREFIX}/${INSTALL_LOCATION}/usr/lib\"\n    mv \"${DSTROOT}${INSTALL_PATH_PREFIX}/${INSTALL_LOCATION}/usr/local/lib/dsc_extractor.bundle\" \"${DSTROOT}${INSTALL_PATH_PREFIX}/${INSTALL_LOCATION}/usr/lib/dsc_extractor.bundle\"\nfi";
+                       shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n     # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n     # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\n\trm -rf ${DSTROOT}/usr/lib\nfi\n";
                        showEnvVarsInLog = 0;
                };
                F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
                F93937350A94FB2900070A07 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD_64_BIT)";
                                CLANG_CXX_LIBRARY = "libc++";
                                COPY_PHASE_STRIP = NO;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
                                OTHER_CPLUSPLUSFLAGS = (
+                                       "-std=c++11",
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                );
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PRODUCT_NAME = update_dyld_shared_cache;
+                               SDKROOT = macosx.internal;
                                VALID_ARCHS = "x86_64 i386";
                        };
                        name = Debug;
                };
                F93937360A94FB2900070A07 /* Release */ = {
                        isa = XCBuildConfiguration;
+                       baseConfigurationReference = F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */;
                        buildSettings = {
                                ARCHS = x86_64;
                                CLANG_CXX_LIBRARY = "libc++";
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
-                               INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/$(LOCAL)/bin";
-                               LOCAL = "$(LOCAL_$(RC_TARGET_CONFIG))";
-                               LOCAL_iPhone = local;
                                OTHER_CPLUSPLUSFLAGS = (
+                                       "-std=c++11",
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                );
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PRODUCT_NAME = update_dyld_shared_cache;
+                               SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                VALID_ARCHS = "x86_64 i386";
                                GCC_MODEL_TUNING = G5;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
-                               INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/bin";
+                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
                        };
                        name = Debug;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
-                               INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/bin";
+                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
                                SKIP_INSTALL = NO;
                        };
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
                                GCC_OPTIMIZATION_LEVEL = 0;
-                               INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/lib";
+                               INSTALL_PATH = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/lib";
                                MACH_O_TYPE = mh_bundle;
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                DYLIB_CURRENT_VERSION = "";
                                EXECUTABLE_EXTENSION = bundle;
                                GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
-                               INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/lib";
+                               INSTALL_PATH = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/lib";
                                MACH_O_TYPE = mh_bundle;
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                };
                F9D8C7DE087B087300E93EFB /* Debug */ = {
                        isa = XCBuildConfiguration;
-                       baseConfigurationReference = F97988FA187F706600EC2C8E /* dyld.xcconfig */;
+                       baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
                                        "$(ALIGNMENT)",
                                        "$(ENTRY)",
                                );
+                               SDKROOT = macosx.internal;
                                STRIPFLAGS = "-S";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                };
                F9D8C7E0087B087300E93EFB /* Release */ = {
                        isa = XCBuildConfiguration;
-                       baseConfigurationReference = F97988FA187F706600EC2C8E /* dyld.xcconfig */;
+                       baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
                        buildSettings = {
                                CLANG_CXX_LIBRARY = "libc++";
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                        "$(ALIGNMENT)",
                                        "$(ENTRY)",
                                );
+                               SDKROOT = macosx.internal;
                                STRIPFLAGS = "-S";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                };
                F9D8C7E2087B087300E93EFB /* Debug */ = {
                        isa = XCBuildConfiguration;
-                       baseConfigurationReference = F97988FB187F707A00EC2C8E /* libdyld.xcconfig */;
+                       baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
                                ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
                                DEAD_CODE_STRIPPING = YES;
                                GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
                                INSTALLHDRS_COPY_PHASE = YES;
+                               OTHER_CFLAGS = "-mno-global-merge";
                                OTHER_LDFLAGS = (
                                        "-nostdlib",
                                        "$(LIBSYSTEM_LIBS)",
                };
                F9D8C7E4087B087300E93EFB /* Release */ = {
                        isa = XCBuildConfiguration;
-                       baseConfigurationReference = F97988FB187F707A00EC2C8E /* libdyld.xcconfig */;
+                       baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
                                COPY_PHASE_STRIP = YES;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
                                HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
                                INSTALLHDRS_COPY_PHASE = YES;
+                               OTHER_CFLAGS = "-mno-global-merge";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-fno-exceptions",
                                        "$(OTHER_CFLAGS)",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
                                );
+                               "OTHER_LDFLAGS[sdk=iphoneos*]" = (
+                                       "-nostdlib",
+                                       "$(LIBSYSTEM_LIBS)",
+                                       "-umbrella",
+                                       System,
+                                       "-L$(SDKROOT)/usr/lib/system",
+                                       "-Wl,-dirty_data_list,$(SRCROOT)/src/libdyld_data_symbols.dirty",
+                               );
+                               "OTHER_LDFLAGS[sdk=macosx*]" = (
+                                       "-nostdlib",
+                                       "$(LIBSYSTEM_LIBS)",
+                                       "-umbrella",
+                                       System,
+                                       "-L$(SDKROOT)/usr/lib/system",
+                               );
                                PRODUCT_NAME = dyld;
                                SEPARATE_STRIP = YES;
                                SKIP_INSTALL = NO;
                };
                F9D8C7EA087B087300E93EFB /* Debug */ = {
                        isa = XCBuildConfiguration;
-                       baseConfigurationReference = 3FC074DF188330C8005F11DD /* base.xcconfig */;
+                       baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
                                CLANG_CXX_LIBRARY = "compiler-default";
                        };
                };
                F9D8C7EC087B087300E93EFB /* Release */ = {
                        isa = XCBuildConfiguration;
-                       baseConfigurationReference = 3FC074DF188330C8005F11DD /* base.xcconfig */;
+                       baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
                                CLANG_CXX_LIBRARY = "compiler-default";
                        };
                                GCC_MODEL_TUNING = G5;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
-                               INSTALL_PATH_ACTUAL = /usr/local/lib;
+                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
                                GCC_WARN_UNINITIALIZED_AUTOS = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                INSTALLHDRS_COPY_PHASE = YES;
-                               INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/lib";
+                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "-stdlib=libc++",
                                        "$(OTHER_CFLAGS)",
index 24208db58d08c5ad3f917a6aa531b2ce77e6fb7e..7708927689ba75359e9d2ad3e38de920ef9b67db 100644 (file)
@@ -171,6 +171,14 @@ extern bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections*
 extern const char* dyld_image_path_containing_address(const void* addr);
 
 
+//
+// This is an optimized form of dladdr() that only returns the dli_fbase field.
+// Return NULL, if address is not in any image tracked by dyld.
+//
+// Exists in Mac OS X 10.11 and later
+extern const struct mach_header* dyld_image_header_containing_address(const void* addr);
+
+
 
 // Convienence constants for return values from dyld_get_sdk_version() and friends.
 #define DYLD_MACOSX_VERSION_10_4               0x000A0400
@@ -180,6 +188,7 @@ extern const char* dyld_image_path_containing_address(const void* addr);
 #define DYLD_MACOSX_VERSION_10_8               0x000A0800
 #define DYLD_MACOSX_VERSION_10_9               0x000A0900
 #define DYLD_MACOSX_VERSION_10_10              0x000A0A00
+#define DYLD_MACOSX_VERSION_10_11              0x000A0B00
 
 #define DYLD_IOS_VERSION_2_0           0x00020000
 #define DYLD_IOS_VERSION_2_1           0x00020100
@@ -198,9 +207,13 @@ extern const char* dyld_image_path_containing_address(const void* addr);
 #define DYLD_IOS_VERSION_7_0           0x00070000
 #define DYLD_IOS_VERSION_7_1           0x00070100
 #define DYLD_IOS_VERSION_8_0           0x00080000
+#define DYLD_IOS_VERSION_8_1           0x00080100
+#define DYLD_IOS_VERSION_8_2           0x00080200
+#define DYLD_IOS_VERSION_9_0           0x00090000
+
 
 //
-// This is finds the SDK version a binary was built against.
+// This finds the SDK version a binary was built against.
 // Returns zero on error, or if SDK version could not be determined.
 //
 // Exists in Mac OS X 10.8 and later 
@@ -209,16 +222,26 @@ extern uint32_t dyld_get_sdk_version(const struct mach_header* mh);
 
 
 //
-// This is finds the SDK version the main executable was built against.
+// This finds the SDK version that the main executable was built against.
 // Returns zero on error, or if SDK version could not be determined.
 //
+// Note on WatchOS, this returns the equivalent iOS SDK version number
+// (i.e an app built against WatchOS 2.0 SDK returne 9.0).  To see the
+// platform specific sdk version use dyld_get_program_sdk_watch_os_version().
+//
 // Exists in Mac OS X 10.8 and later 
 // Exists in iOS 6.0 and later
 extern uint32_t dyld_get_program_sdk_version();
 
 
+// Watch OS only.
+// This finds the Watch OS SDK version that the main executable was built against.
+// Exists in Watch OS 2.0 and later
+extern uint32_t dyld_get_program_sdk_watch_os_version(); // __WATCHOS_AVAILABLE(2.0);
+
+
 //
-// This is finds the min OS version a binary was built to run on.
+// This finds the min OS version a binary was built to run on.
 // Returns zero on error, or if no min OS recorded in binary.
 //
 // Exists in Mac OS X 10.8 and later 
@@ -227,7 +250,7 @@ extern uint32_t dyld_get_min_os_version(const struct mach_header* mh);
 
 
 //
-// This is finds the min OS version the main executable was built to run on.
+// This finds the min OS version the main executable was built to run on.
 // Returns zero on error, or if no min OS recorded in binary.
 //
 // Exists in Mac OS X 10.8 and later 
@@ -253,6 +276,15 @@ extern bool dyld_shared_cache_some_image_overridden();
 extern bool dyld_process_is_restricted();
 
 
+
+//
+// Returns path used by dyld for standard dyld shared cache file for the current arch.
+//
+// Exists in Mac OS X 10.11 and later
+extern const char* dyld_shared_cache_file_path();
+
+
+
 //
 // <rdar://problem/13820686> for OpenGL to tell dyld it is ok to deallocate a memory based image when done.
 //
index 8d59bfb4a197f7682532fc57fb1afa0c0c4e6ec8..d2a27dfe607c4067c0a9b76fea967293274f5c86 100644 (file)
@@ -145,6 +145,9 @@ struct hashstr {
 // (used for selector names and class names)
 typedef std::unordered_map<const char *, uint64_t, hashstr, eqstr> string_map;
 
+// protocol name => protocol vmaddress
+typedef std::unordered_map<const char *, uint64_t, hashstr, eqstr> protocol_map;
+
 // class name => (class vmaddress, header_info vmaddress)
 typedef std::unordered_multimap<const char *, std::pair<uint64_t, uint64_t>, hashstr, eqstr> class_map;
 
@@ -161,8 +164,8 @@ struct objc_stringhash_t {
     uint32_t occupied;
     uint32_t shift;
     uint32_t mask;
-    uint32_t zero;
-    uint32_t unused; // alignment pad
+    uint32_t unused1;  // was zero
+    uint32_t unused2;  // alignment pad
     uint64_t salt;
     
     uint32_t scramble[256];
@@ -221,9 +224,8 @@ struct objc_stringhash_t {
         if (check_fail) return INDEX_NOT_FOUND;
 #endif
 
-        // fixme change &zero to 0 in the next version-breaking update
         objc_stringhash_offset_t offset = offsets()[h];
-        if (offset == offsetof(objc_stringhash_t,zero)) return INDEX_NOT_FOUND;
+        if (offset == 0) return INDEX_NOT_FOUND;
         const char *result = (const char *)this + offset;
         if (0 != strcmp(key, result)) return INDEX_NOT_FOUND;
 
@@ -259,7 +261,6 @@ struct objc_stringhash_t {
         S32(occupied);
         S32(shift);
         S32(mask);
-        S32(zero);
         S64(salt);
     }
 
@@ -284,8 +285,8 @@ struct objc_stringhash_t {
         occupied = phash.occupied;
         shift = phash.shift;
         mask = phash.mask;
-        zero = 0;
-        unused = 0;
+        unused1 = 0;
+        unused2 = 0;
         salt = phash.salt;
 
         if (size() > remaining) {
@@ -300,10 +301,9 @@ struct objc_stringhash_t {
             tab[i] = phash.tab[i];
         }
         
-        // Set offsets to ""
+        // Set offsets to 0
         for (uint32_t i = 0; i < phash.capacity; i++) {
-            offsets()[i] = 
-                (objc_stringhash_offset_t)offsetof(objc_stringhash_t, zero);
+            offsets()[i] = 0;
         }
         // Set checkbytes to 0
         for (uint32_t i = 0; i < phash.capacity; i++) {
@@ -469,12 +469,10 @@ struct objc_clsopt_t : objc_stringhash_t {
             return "selector section too small (metadata not optimized)";
         }
 
-        // Set class offsets to &zero
-        objc_stringhash_offset_t zeroOffset = 
-            (objc_stringhash_offset_t)offsetof(objc_stringhash_t, zero);
+        // Set class offsets to 0
         for (uint32_t i = 0; i < capacity; i++) {
-            classOffsets()[i].clsOffset = zeroOffset;
-            classOffsets()[i].hiOffset = zeroOffset;
+            classOffsets()[i].clsOffset = 0;
+            classOffsets()[i].hiOffset = 0;
         }
         
         // Set real class offsets
@@ -486,7 +484,7 @@ struct objc_clsopt_t : objc_stringhash_t {
                 return "class list busted (metadata not optimized)";
             }
 
-            if (classOffsets()[h].clsOffset != zeroOffset) {
+            if (classOffsets()[h].clsOffset != 0) {
                 // already did this class
                 continue;
             }
@@ -551,6 +549,88 @@ struct objc_clsopt_t : objc_stringhash_t {
 #endif
 };
 
+
+
+struct objc_protocolopt_t : objc_stringhash_t {
+    // ...objc_stringhash_t fields...
+    // uint32_t protocolOffsets[capacity]; /* offsets from &capacity to protocol_t */
+
+    objc_stringhash_offset_t *protocolOffsets() { return (objc_stringhash_offset_t *)&offsets()[capacity]; }
+    const objc_stringhash_offset_t *protocolOffsets() const { return (const objc_stringhash_offset_t *)&offsets()[capacity]; }
+
+    void* getProtocol(const char *key) const 
+    {
+        uint32_t h = getIndex(key);
+        if (h == INDEX_NOT_FOUND) { 
+            return NULL;
+        }
+
+        return (void *)((const char *)this + protocolOffsets()[h]);
+    }
+
+#ifdef SELOPT_WRITE
+
+    size_t size() 
+    {
+        return
+            objc_stringhash_t::size() + capacity * sizeof(objc_stringhash_offset_t);
+    }
+
+    void byteswap(bool little_endian) 
+    {
+        objc_stringhash_offset_t *o;
+        
+        o = protocolOffsets();
+        for (objc_stringhash_offset_t i = 0; i < capacity; i++) {
+            S32(o[i]);
+        }
+
+        objc_stringhash_t::byteswap(little_endian);
+    }
+    
+    const char *write(uint64_t base, size_t remaining, 
+                      string_map& strings, protocol_map& protocols, 
+                      bool verbose)
+    {
+        const char *err;
+        err = objc_stringhash_t::write(base, remaining, strings);
+        if (err) return err;
+
+        if (size() > remaining) {
+            return "selector section too small (metadata not optimized)";
+        }
+
+        // Set protocol offsets to 0
+        for (uint32_t i = 0; i < capacity; i++) {
+            protocolOffsets()[i] = 0;
+        }
+        
+        // Set real protocol offsets
+#       define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t))
+        protocol_map::const_iterator c;
+        for (c = protocols.begin(); c != protocols.end(); ++c) {
+            uint32_t h = getIndex(c->first);
+            if (h == INDEX_NOT_FOUND) {
+                return "protocol list busted (metadata not optimized)";
+            }
+
+            int64_t offset = c->second - base;
+            if ((offset<<SHIFT)>>SHIFT != offset) {
+                return "protocol offset too big (metadata not optimized)";
+            }
+
+            protocolOffsets()[h] = (objc_stringhash_offset_t)offset;
+        }
+#       undef SHIFT
+        
+        return NULL;
+    }
+
+// SELOPT_WRITE
+#endif
+};
+
+
 // Precomputed image list.
 struct objc_headeropt_t;
 
@@ -558,15 +638,16 @@ struct objc_headeropt_t;
 struct objc_clsopt_t;
 
 // Edit objc-sel-table.s if you change this value.
-enum { VERSION = 12 };
+enum { VERSION = 13 };
 
 // Top-level optimization structure.
 // Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure.
-struct objc_opt_t {
+struct alignas(alignof(void*)) objc_opt_t {
     uint32_t version;
     int32_t selopt_offset;
     int32_t headeropt_offset;
     int32_t clsopt_offset;
+    int32_t protocolopt_offset;
 
     const objc_selopt_t* selopt() const { 
         if (selopt_offset == 0) return NULL;
@@ -586,6 +667,11 @@ struct objc_opt_t {
         if (clsopt_offset == 0) return NULL;
         return (objc_clsopt_t *)((uint8_t *)this + clsopt_offset);
     }
+
+    struct objc_protocolopt_t* protocolopt() const { 
+        if (protocolopt_offset == 0) return NULL;
+        return (objc_protocolopt_t *)((uint8_t *)this + protocolopt_offset);
+    }
 };
 
 // sizeof(objc_opt_t) must be pointer-aligned
@@ -602,9 +688,18 @@ STATIC_ASSERT(sizeof(objc_opt_t) % sizeof(void*) == 0);
         4, 4, 63, 3, 0, 0, 0,0, X256(0), 0, 0, 16, 16, 16, 16       \
         /* no objc_headeropt_t */                                   \
         /* no objc_clsopt_t */                                      \
+        /* no objc_protocolopt_t */                                 \
 }
 
 
+// List of offsets in libobjc that the shared cache optimization needs to use.
+template <typename T>
+struct objc_opt_pointerlist_tt {
+    T protocolClass;
+};
+typedef struct objc_opt_pointerlist_tt<uintptr_t> objc_opt_pointerlist_t;
+
+
 /*
 --------------------------------------------------------------------
 mix -- mix 3 64-bit values reversibly.
index cd2524eb525baf0573238e4ddb4d8aeffc35f871..c6aca25a8b0de14e79b75c995d59bea8e169a860 100644 (file)
@@ -70,6 +70,9 @@ public:
 
        const uint8_t*  uuid() const                                                    INLINE { return fields.uuid; }
        void                    set_uuid(const uint8_t value[16])               INLINE { memcpy(fields.uuid, value, 16); }
+       
+       uint64_t                cacheType() const                               INLINE { return E::get64(fields.cacheType); }
+       void                    set_cacheType(uint64_t value)   INLINE { E::set64(fields.cacheType, value); }
 
 private:
        dyld_cache_header                       fields;
index 2b8a3ac5f95ba2ee082f311cb5060f3687f5f8e1..d87fe233b8c493f65043424d413c645cff47481a 100644 (file)
@@ -77,7 +77,7 @@ public:
        typedef std::unordered_map<const char*, class Binder<A>*, CStringHash, CStringEquals> Map;
 
 
-                                                                                               Binder(const MachOLayoutAbstraction&, uint64_t dyldBaseAddress);
+                                                                                               Binder(const MachOLayoutAbstraction&);
        virtual                                                                         ~Binder() {}
        
        const char*                                                                     getDylibID() const;
@@ -93,14 +93,10 @@ private:
        struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; };
        typedef std::unordered_map<const char*, pint_t, CStringHash, CStringEquals> NameToAddrMap;
        typedef std::unordered_set<const char*, CStringHash, CStringEquals> NameSet;
-    struct ClientAndSymbol { Binder<A>* client; const char* symbolName; };
-    struct SymbolAndLazyPointer { const char* symbolName; pint_t lpVMAddr; };
+       typedef std::unordered_map<const char*, std::set<Binder<A>*>, CStringHash, CStringEquals> ResolverClientsMap;
+
        
        static bool                                                                     isPublicLocation(const char* pth);
-       void                                                                            doBindExternalRelocations();
-       void                                                                            doBindIndirectSymbols();
-       void                                                                            doSetUpDyldSection();
-       void                                                                            doSetPreboundUndefines();
        void                                                                            hoistPrivateRexports();
        int                                                                                     ordinalOfDependentBinder(Binder<A>* dep);
        void                                                                            doBindDyldInfo(std::vector<void*>& pointersInData);
@@ -109,15 +105,14 @@ private:
                                                                                                                                int libraryOrdinal, int64_t addend, 
                                                                                                                                const char* symbolName, bool lazyPointer, bool weakImport,
                                                                                                                                std::vector<void*>& pointersInData);
-       pint_t                                                                          resolveUndefined(const macho_nlist<P>* undefinedSymbol);
        bool                                                                            findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn, 
                                                                                                                                                        bool* isResolverSymbol, bool* isAbsolute);
-       void                                                                            bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value);
        const char*                                                                     parentUmbrella();
        pint_t                                                                          runtimeAddressFromNList(const macho_nlist<P>* sym);
-    void                                        optimizeStub(const char* symbolName, pint_t lpVMAddr);
-    void                                        optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
+    void                                        switchStubToUseSharedLazyPointer(const char* symbolName, pint_t lpVMAddr);
+    void                                        switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
     pint_t                                      findLazyPointerFor(const char* symbolName);
+       void                                                                            shareLazyPointersToResolvers();
 
 
        static uint8_t                                                          pointerRelocSize();
@@ -128,7 +123,6 @@ private:
        NameSet                                                                         fSymbolResolvers;
        NameSet                                                                         fAbsoluteSymbols;
        std::vector<SymbolReExport>                                     fReExportedSymbols;
-       uint64_t                                                                        fDyldBaseAddress;
        const macho_nlist<P>*                                           fSymbolTable;
        const char*                                                                     fStrings;
        const macho_dysymtab_command<P>*                        fDynamicInfo;
@@ -138,8 +132,7 @@ private:
        const macho_dyld_info_command<P>*                       fDyldInfo;
        bool                                                                            fOriginallyPrebound;
        bool                                                                            fReExportedSymbolsResolved;
-    std::vector<ClientAndSymbol>                fClientAndSymbols;
-       NameToAddrMap                                                           fResolverLazyPointers;
+       ResolverClientsMap                                                      fResolverInfo;
 };
 
 template <> 
@@ -159,8 +152,8 @@ typename A::P::uint_t       Binder<A>::runtimeAddressFromNList(const macho_nlist<P>* s
 
 
 template <typename A>
-Binder<A>::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress)
-       : Rebaser<A>(layout), fDyldBaseAddress(dyldBaseAddress),
+Binder<A>::Binder(const MachOLayoutAbstraction& layout)
+       : Rebaser<A>(layout),
          fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL),
          fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL),
          fParentUmbrella(NULL), fReExportedSymbolsResolved(false)
@@ -202,7 +195,7 @@ Binder<A>::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress
                                fDyldInfo = (macho_dyld_info_command<P>*)cmd;
                                break;
                        case LC_RPATH:
-                               throwf("dyld shared cache does not support LC_RPATH found in %s", layout.getFilePath());
+                               fprintf(stderr, "update_dyld_shared_cache: warning: dyld shared cache does not support LC_RPATH found in %s\n", layout.getFilePath());
                                break;
                        default:
                                if ( cmd->cmd() & LC_REQ_DYLD )
@@ -214,72 +207,48 @@ Binder<A>::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress
                throw "no LC_DYSYMTAB";
        if ( fSymbolTable == NULL )     
                throw "no LC_SYMTAB";
+       if ( fDyldInfo == NULL )        
+               throw "no LC_DYLD_INFO";
        // build hash table
-//     fprintf(stderr, "exports for %s\n", layout.getFilePath());
-       if ( fDyldInfo != NULL ) {
-               std::vector<mach_o::trie::Entry> exports;
-               const uint8_t* exportsStart = layout.getDyldInfoExports(); 
-               const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()];
-               mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
-               pint_t baseAddress = layout.getSegments()[0].newAddress();
-               for(std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
-                       switch ( it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
-                               case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
-                                       if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
-                                               fSymbolResolvers.insert(it->name);
-                                       }
-                                       if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
-                                               //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID());
-                                               SymbolReExport sym;
-                                               sym.exportName = it->name;
-                                               sym.dylibOrdinal = it->other;
-                                               sym.importName = it->importName;
-                                               if ( (sym.importName == NULL) || (sym.importName[0] == '\0') ) 
-                                                       sym.importName = sym.exportName;
-                                               fReExportedSymbols.push_back(sym);
-                                               // fHashTable entry will be added in first call to findExportedSymbolAddress() 
-                                       }
-                                       else {
-                                               fHashTable[it->name] = it->address + baseAddress;
-                                       }
-                                       break;
-                               case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
+       //fprintf(stderr, "exports for %s\n", layout.getFilePath());
+       std::vector<mach_o::trie::Entry> exports;
+       const uint8_t* exportsStart = layout.getDyldInfoExports(); 
+       const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()];
+       mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
+       pint_t baseAddress = layout.getSegments()[0].newAddress();
+       for(std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
+               switch ( it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
+                       case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
+                               if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
+                                       fSymbolResolvers.insert(it->name);
+                               }
+                               if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+                                       //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID());
+                                       SymbolReExport sym;
+                                       sym.exportName = it->name;
+                                       sym.dylibOrdinal = it->other;
+                                       sym.importName = it->importName;
+                                       if ( (sym.importName == NULL) || (sym.importName[0] == '\0') ) 
+                                               sym.importName = sym.exportName;
+                                       fReExportedSymbols.push_back(sym);
+                                       // fHashTable entry will be added in first call to findExportedSymbolAddress() 
+                               }
+                               else {
                                        fHashTable[it->name] = it->address + baseAddress;
-                                       break;
-                               case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
-                                       fHashTable[it->name] = it->address;
-                                       fAbsoluteSymbols.insert(it->name);
-                                       break;
-                               default:
-                                       throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath());
-                                       break;
-                       }
-                       //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name);
-               }
-       }
-       else {
-               if ( fDynamicInfo->tocoff() == 0 ) {
-                       const macho_nlist<P>* start = &fSymbolTable[fDynamicInfo->iextdefsym()];
-                       const macho_nlist<P>* end = &start[fDynamicInfo->nextdefsym()];
-                       fHashTable.reserve(fDynamicInfo->nextdefsym()); // set initial bucket count
-                       for (const macho_nlist<P>* sym=start; sym < end; ++sym) {
-                               const char* name = &fStrings[sym->n_strx()];
-                               fHashTable[name] = runtimeAddressFromNList(sym);
-                               //fprintf(stderr, " 0x%08llX %s\n", sym->n_value(), name);
-                       }
-               }
-               else {
-                       int32_t count = fDynamicInfo->ntoc();
-                       fHashTable.reserve(count); // set initial bucket count
-                       const struct dylib_table_of_contents* toc = (dylib_table_of_contents*)&this->fLinkEditBase[fDynamicInfo->tocoff()];
-                       for (int32_t i = 0; i < count; ++i) {
-                               const uint32_t index = E::get32(toc[i].symbol_index);
-                               const macho_nlist<P>* sym = &fSymbolTable[index];
-                               const char* name = &fStrings[sym->n_strx()];
-                               fHashTable[name] = runtimeAddressFromNList(sym);
-                               //fprintf(stderr, "- 0x%08llX %s\n", sym->n_value(), name);
-                       }
+                               }
+                               break;
+                       case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
+                               fHashTable[it->name] = it->address + baseAddress;
+                               break;
+                       case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
+                               fHashTable[it->name] = it->address;
+                               fAbsoluteSymbols.insert(it->name);
+                               break;
+                       default:
+                               throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath());
+                               break;
                }
+               //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name);
        }
 }
 
@@ -341,7 +310,7 @@ bool Binder<A>::isPublicLocation(const char* pth)
 template <typename A>
 void Binder<A>::setDependentBinders(const Map& map)
 {
-       // first pass to build vector of dylibs
+       // build vector of dependent dylibs
        const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
        const uint32_t cmd_count = this->fHeader->ncmds();
        const macho_load_command<P>* cmd = cmds;
@@ -400,64 +369,7 @@ void Binder<A>::setDependentBinders(const Map& map)
                                break;
                }
                cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }
-       // handle pre-10.5 re-exports 
-       if ( (this->fHeader->flags() & MH_NO_REEXPORTED_DYLIBS) == 0 ) {
-               cmd = cmds;
-               // LC_SUB_LIBRARY means re-export one with matching leaf name
-               const char* dylibBaseName;
-               const char* frameworkLeafName;
-               for (uint32_t i = 0; i < cmd_count; ++i) {
-                       switch ( cmd->cmd() ) {
-                               case LC_SUB_LIBRARY:
-                                       dylibBaseName = ((macho_sub_library_command<P>*)cmd)->sub_library();
-                                       for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
-                                               const char* dylibName = it->binder->getDylibID();
-                                               const char* lastSlash = strrchr(dylibName, '/');
-                                               const char* leafStart = &lastSlash[1];
-                                               if ( lastSlash == NULL )
-                                                       leafStart = dylibName;
-                                               const char* firstDot = strchr(leafStart, '.');
-                                               int len = strlen(leafStart);
-                                               if ( firstDot != NULL )
-                                                       len = firstDot - leafStart;
-                                               if ( strncmp(leafStart, dylibBaseName, len) == 0 )
-                                                       it->reExport = true;
-                                       }
-                                       break;
-                               case LC_SUB_UMBRELLA:
-                                       frameworkLeafName = ((macho_sub_umbrella_command<P>*)cmd)->sub_umbrella();
-                                       for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
-                                               const char* dylibName = it->binder->getDylibID();
-                                               const char* lastSlash = strrchr(dylibName, '/');
-                                               if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) )
-                                                       it->reExport = true;
-                                       }
-                                       break;
-                       }
-                       cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-               }
-               // ask dependents if they re-export through me
-               const char* thisName = this->getDylibID();
-               if ( thisName != NULL ) {
-                       const char* thisLeafName = strrchr(thisName, '/');
-                       if ( thisLeafName != NULL )
-                               ++thisLeafName;
-                       for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
-                               if ( ! it->reExport ) {
-                                       Binder<A>* dep = it->binder;
-                                       if ( dep != NULL ) {
-                                               const char* parentUmbrellaName = dep->parentUmbrella();
-                                               if ( parentUmbrellaName != NULL ) {
-                                                       if ( strcmp(parentUmbrellaName, thisLeafName) == 0 )
-                                                               it->reExport = true;
-                                               }
-                                       }
-                               }
-                       }       
-               }
-       }
-       
+       }       
 }
 
 template <typename A>
@@ -523,47 +435,9 @@ void Binder<A>::hoistPrivateRexports()
 template <typename A>
 void Binder<A>::bind(std::vector<void*>& pointersInData)
 {
-       this->doSetUpDyldSection();
-       if ( fDyldInfo != NULL ) {
-               this->doBindDyldInfo(pointersInData);
-               this->doBindDyldLazyInfo(pointersInData);
-               this->hoistPrivateRexports();
-               // weak bind info is processed at launch time
-       }
-       else {
-               this->doBindExternalRelocations();
-               this->doBindIndirectSymbols();
-               this->doSetPreboundUndefines();
-       }
-}
-
-
-template <typename A>
-void Binder<A>::doSetUpDyldSection()
-{
-       // find __DATA __dyld section 
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = this->fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                       const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                       if ( strcmp(seg->segname(), "__DATA") == 0 ) {
-                               const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)seg + sizeof(macho_segment_command<P>));
-                               const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                               for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                                       if ( (strcmp(sect->sectname(), "__dyld") == 0) && (sect->size() >= 2*sizeof(pint_t)) ) {
-                                               // set two values in __dyld section to point into dyld
-                                               pint_t* lazyBinder = this->mappedAddressForNewAddress(sect->addr());
-                                               pint_t* dyldFuncLookup = this->mappedAddressForNewAddress(sect->addr()+sizeof(pint_t));
-                                               A::P::setP(*lazyBinder, fDyldBaseAddress + 0x1000);
-                                               A::P::setP(*dyldFuncLookup, fDyldBaseAddress + 0x1008);
-                                       }
-                               }
-                       }
-               }
-               cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }
+       this->doBindDyldInfo(pointersInData);
+       this->doBindDyldLazyInfo(pointersInData);
+       // weak bind info is processed at launch time
 }
 
 template <typename A>
@@ -793,232 +667,6 @@ void Binder<A>::doBindDyldInfo(std::vector<void*>& pointersInData)
                        default:
                                throwf("bad bind opcode %d", *p);
                }
-       }       
-
-       
-
-}
-
-
-template <typename A>
-void Binder<A>::doSetPreboundUndefines()
-{
-       const macho_dysymtab_command<P>* dysymtab = NULL;
-       macho_nlist<P>* symbolTable = NULL;
-
-       // get symbol table info
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = this->fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               switch (cmd->cmd()) {
-                       case LC_SYMTAB:
-                               {
-                                       const macho_symtab_command<P>* symtab = (macho_symtab_command<P>*)cmd;
-                                       symbolTable = (macho_nlist<P>*)(&this->fLinkEditBase[symtab->symoff()]);
-                               }
-                               break;
-                       case LC_DYSYMTAB:
-                               dysymtab = (macho_dysymtab_command<P>*)cmd;
-                               break;
-               }
-               cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }       
-       
-       // walk all undefines and set their prebound n_value
-       macho_nlist<P>* const lastUndefine = &symbolTable[dysymtab->iundefsym()+dysymtab->nundefsym()];
-       for (macho_nlist<P>* entry = &symbolTable[dysymtab->iundefsym()]; entry < lastUndefine; ++entry) {
-               if ( entry->n_type() & N_EXT ) {
-                       //fprintf(stderr, "doSetPreboundUndefines: r_sym=%s, pbaddr=0x%08X, in %s\n", 
-                       //      &fStrings[entry->n_strx()], pbaddr, this->getDylibID());
-                       pint_t pbaddr = this->resolveUndefined(entry);
-                       entry->set_n_value(pbaddr);
-               }
-       }
-}
-
-
-template <typename A>
-void Binder<A>::doBindExternalRelocations()
-{
-       // get where reloc addresses start
-       // these address are always relative to first writable segment because they are in cache which always
-       // has writable segments far from read-only segments
-       pint_t firstWritableSegmentBaseAddress = 0;
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = this->fLayout.getSegments();
-       for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
-               const MachOLayoutAbstraction::Segment& seg = *it;
-               if ( seg.writable() ) {
-                       firstWritableSegmentBaseAddress = seg.newAddress();
-                       break;
-               }
-       }       
-       
-       // loop through all external relocation records and bind each
-       const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(&this->fLinkEditBase[fDynamicInfo->extreloff()]);
-       const macho_relocation_info<P>* const relocsEnd = &relocsStart[fDynamicInfo->nextrel()];
-       for (const macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
-               if ( reloc->r_length() != pointerRelocSize() ) 
-                       throw "bad external relocation length";
-               if ( reloc->r_type() != pointerRelocType() ) 
-                       throw "unknown external relocation type";
-               if ( reloc->r_pcrel() ) 
-                       throw "r_pcrel external relocaiton not supported";
-
-               const macho_nlist<P>* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum()];
-               pint_t* location;
-               try {
-                       location = this->mappedAddressForNewAddress(reloc->r_address() + firstWritableSegmentBaseAddress);
-               }
-               catch (const char* msg) {
-                       throwf("%s processesing external relocation r_address 0x%08X", msg, reloc->r_address());
-               }
-               pint_t addend =  P::getP(*location);
-               if ( fOriginallyPrebound ) {
-                       // in a prebound binary, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound
-                       // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address
-                       // if mach-o relocation structs had an "addend" field this complication would not be necessary.
-                       addend -= undefinedSymbol->n_value();
-                       // To further complicate things, if this is defined symbol, then its n_value has already been adjust to the
-                       // new base address, so we need to back off the slide too..
-                       if ( (undefinedSymbol->n_type() & N_TYPE) == N_SECT ) {
-                               addend += this->getSlideForNewAddress(undefinedSymbol->n_value());
-                       }
-               }
-               pint_t symbolAddr = this->resolveUndefined(undefinedSymbol);
-               //fprintf(stderr, "external reloc: r_address=0x%08X, r_sym=%s, symAddr=0x%08llX, addend=0x%08llX in %s\n", 
-               //              reloc->r_address(), &fStrings[undefinedSymbol->n_strx()], (uint64_t)symbolAddr, (uint64_t)addend, this->getDylibID());
-               P::setP(*location, symbolAddr + addend); 
-       }
-}
-
-
-// most architectures use pure code, unmodifiable stubs
-template <typename A>
-void Binder<A>::bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value)
-{
-       // do nothing
-}
-
-// x86 supports fast stubs
-template <>
-void Binder<x86>::bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value)
-{
-       // if the stub is not 5-bytes, it is an old slow stub
-       if ( elementSize == 5 ) {
-               uint32_t rel32 = value - (vmlocation + 5);
-               location[0] = 0xE9; // JMP rel32
-               location[1] = rel32 & 0xFF;
-               location[2] = (rel32 >> 8) & 0xFF;
-               location[3] = (rel32 >> 16) & 0xFF;
-               location[4] = (rel32 >> 24) & 0xFF;
-       }
-}
-
-template <typename A>
-void Binder<A>::doBindIndirectSymbols()
-{
-       const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()];
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = this->fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       //fprintf(stderr, "doBindIndirectSymbols() %s\n", this->fLayout.getFilePath());
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                       const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                       const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)seg + sizeof(macho_segment_command<P>));
-                       const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                       for (const macho_section<P>* sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                               uint8_t elementSize = 0;
-                               uint8_t sectionType = sect->flags() & SECTION_TYPE;
-                               switch ( sectionType ) {
-                                       case S_SYMBOL_STUBS:
-                                               elementSize = sect->reserved2();
-                                               break;
-                                       case S_NON_LAZY_SYMBOL_POINTERS:
-                                       case S_LAZY_SYMBOL_POINTERS:
-                                               elementSize = sizeof(pint_t);
-                                               break;
-                               }
-                               if ( elementSize != 0 ) {
-                                       uint32_t elementCount = sect->size() / elementSize;
-                                       const uint32_t indirectTableOffset = sect->reserved1();
-                                       uint8_t* location = NULL; 
-                                       if ( sect->size() != 0 )
-                                               location = (uint8_t*)this->mappedAddressForNewAddress(sect->addr());
-                                       pint_t vmlocation = sect->addr();
-                                       for (uint32_t j=0; j < elementCount; ++j, location += elementSize, vmlocation += elementSize) {
-                                               uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); 
-                                               switch ( symbolIndex ) {
-                                                       case INDIRECT_SYMBOL_ABS:
-                                                       case INDIRECT_SYMBOL_LOCAL:
-                                                               break;
-                                                       default:
-                                                               const macho_nlist<P>* undefinedSymbol = &fSymbolTable[symbolIndex];
-                                                               //fprintf(stderr, " sect=%s, index=%d, symbolIndex=%d, sym=%s\n", sect->sectname(), j, symbolIndex, &fStrings[undefinedSymbol->n_strx()]);
-                                                               pint_t symbolAddr = this->resolveUndefined(undefinedSymbol);
-                                                               switch ( sectionType ) {
-                                                                       case S_NON_LAZY_SYMBOL_POINTERS:
-                                                                       case S_LAZY_SYMBOL_POINTERS:
-                                                                               P::setP(*((pint_t*)location), symbolAddr); 
-                                                                               break;
-                                                                       case S_SYMBOL_STUBS:
-                                                                               this->bindStub(elementSize, location, vmlocation, symbolAddr);
-                                                                                       break;
-                                                               }
-                                                               break;
-                                               }
-                                       }
-                               }
-                       }
-               }
-               cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }
-}
-
-
-
-
-template <typename A>
-typename A::P::uint_t Binder<A>::resolveUndefined(const macho_nlist<P>* undefinedSymbol)
-{
-       if ( (undefinedSymbol->n_type() & N_TYPE) == N_SECT ) {
-               if ( (undefinedSymbol->n_type() & N_PEXT) != 0 ) {
-                       // is a multi-module private_extern internal reference that the linker did not optimize away
-                       return runtimeAddressFromNList(undefinedSymbol);
-               }
-               if ( (undefinedSymbol->n_desc() & N_WEAK_DEF) != 0 ) {
-                       // is a weak definition, we should prebind to this one in the same linkage unit
-                       return runtimeAddressFromNList(undefinedSymbol);
-               }
-       }
-       const char* symbolName = &fStrings[undefinedSymbol->n_strx()];
-       if ( (this->fHeader->flags() & MH_TWOLEVEL) == 0 ) {
-               // flat namespace binding
-               throw "flat namespace not supported";
-       }
-       else {
-               uint8_t ordinal = GET_LIBRARY_ORDINAL(undefinedSymbol->n_desc());
-               Binder<A>* binder = NULL;
-               switch ( ordinal ) {
-                       case EXECUTABLE_ORDINAL:
-                       case DYNAMIC_LOOKUP_ORDINAL:
-                               throw "magic ordineal not supported";
-                       case SELF_LIBRARY_ORDINAL:
-                               binder = this;
-                               break;
-                       default:
-                               if ( ordinal > fDependentDylibs.size() )
-                                       throw "two-level ordinal out of range";
-                               binder = fDependentDylibs[ordinal-1].binder;
-               }       
-               pint_t addr;
-               bool isResolver;
-               bool isAbsolute;
-        Binder<A>* foundIn;
-               if ( ! binder->findExportedSymbolAddress(symbolName, &addr, &foundIn, &isResolver, &isAbsolute) )
-                       throwf("could not resolve undefined symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID());
-               return addr;
        }
 }
 
@@ -1084,10 +732,7 @@ bool Binder<A>::findExportedSymbolAddress(const char* name, pint_t* result, Bind
 template <typename A>
 void Binder<A>::addResolverClient(Binder<A>* clientDylib, const char* symbolName)
 {
-    ClientAndSymbol x;
-    x.client = clientDylib;
-    x.symbolName = symbolName;
-    fClientAndSymbols.push_back(x);
+       fResolverInfo[symbolName].insert(clientDylib);
 }                                        
 
 
@@ -1095,15 +740,8 @@ template <typename A>
 typename A::P::uint_t Binder<A>::findLazyPointerFor(const char* symbolName)
 {
        static const bool log = false;
-       
-       // first check cache
-       typename NameToAddrMap::iterator pos = fResolverLazyPointers.find(symbolName);
-       if ( pos != fResolverLazyPointers.end() ) {
-               if ( log ) fprintf(stderr, "found cached shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)(pos->second), symbolName, this->getDylibID());
-               return pos->second;
-       }
-       
-       // do slow lookup in lazy pointer section
+
+       // lookup in lazy pointer section
        const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()];
        const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
        const uint32_t cmd_count = this->fHeader->ncmds();
@@ -1130,8 +768,7 @@ typename A::P::uint_t Binder<A>::findLazyPointerFor(const char* symbolName)
                                                                const char* aName = &fStrings[aSymbol->n_strx()];
                                                                //fprintf(stderr, " sect=%s, index=%d, symbolIndex=%d, sym=%s\n", sect->sectname(), j, symbolIndex, &fStrings[undefinedSymbol->n_strx()]);
                                                                if ( strcmp(aName, symbolName) == 0 ) { 
-                                                                       fResolverLazyPointers[symbolName] = vmlocation;
-                                                                       if ( log ) fprintf(stderr, "found slow-path shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID());
+                                                                       if ( log ) fprintf(stderr, "found shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID());
                                                                        return vmlocation;
                                                                }
                                                                break;
@@ -1172,20 +809,30 @@ typename A::P::uint_t Binder<A>::findLazyPointerFor(const char* symbolName)
 template <typename A>
 void Binder<A>::optimize()
 {
-    for (typename std::vector<ClientAndSymbol>::iterator it = fClientAndSymbols.begin(); it != fClientAndSymbols.end(); ++it) {
-        pint_t lpVMAddr = findLazyPointerFor(it->symbolName);
-        if ( lpVMAddr != 0 ) {
-            it->client->optimizeStub(it->symbolName, lpVMAddr);
-        }
-        else {
-                       fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", it->symbolName, it->client->getDylibID());
-        }
-        
-    }
+       hoistPrivateRexports();
+       shareLazyPointersToResolvers();
 }
 
+template <typename A>
+void Binder<A>::shareLazyPointersToResolvers()
+{
+       for (const auto &entry : fResolverInfo) {
+               const char* resolverSymbolName = entry.first;
+        if ( pint_t lpVMAddr = findLazyPointerFor(resolverSymbolName) ) {
+                       for (Binder<A>* clientDylib : entry.second) {
+                               clientDylib->switchStubToUseSharedLazyPointer(resolverSymbolName, lpVMAddr);
+                       }
+               }
+               else {
+                       fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", resolverSymbolName, this->getDylibID());
+               }
+       }
+}
+
+
+
 template <>
-void Binder<arm>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
+void Binder<arm>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
 {
     if ( stubSize != 16 ) {
         fprintf(stderr, "could not optimize ARM stub to resolver function in %s because it is wrong size\n", this->getDylibID());
@@ -1207,7 +854,7 @@ void Binder<arm>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress,
 
 
 template <>
-void Binder<x86_64>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
+void Binder<x86_64>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
 {
     if ( stubSize != 6 ) {
         fprintf(stderr, "could not optimize x86_64 stub to resolver function in %s because it is wrong size\n", this->getDylibID());
@@ -1224,7 +871,7 @@ void Binder<x86_64>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddre
 }
 
 template <typename A>
-void Binder<A>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress)
+void Binder<A>::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress)
 {
     // Remaining architectures are not optimized
     //fprintf(stderr, "optimize stub at %p in %s to use lazyPointer at 0x%llX\n", stubMappedAddress, this->getDylibID(), (uint64_t)lpVMAddress);
@@ -1232,7 +879,7 @@ void Binder<A>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, u
 
 // search for stub in this image that call target symbol name and then optimize its lazy pointer
 template <typename A>
-void Binder<A>::optimizeStub(const char* stubName, pint_t lpVMAddr)
+void Binder<A>::switchStubToUseSharedLazyPointer(const char* stubName, pint_t lpVMAddr)
 {
        // find named stub
        const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()];
@@ -1264,7 +911,7 @@ void Binder<A>::optimizeStub(const char* stubName, pint_t lpVMAddr)
                                    const macho_nlist<P>* sym = &this->fSymbolTable[symbolIndex];
                                    const char* symName = &fStrings[sym->n_strx()];
                                     if ( strcmp(symName, stubName) == 0 ) 
-                                        this->optimizeStub(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr);
+                                        this->switchStubsLazyPointer(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr);
                                 }
                                                                break;
                                                }
index 7a65370a64b6e92c7e5731a2fc174f3312db650b..4cda3d303a75d8591f949e100724d7d27addbd08 100644 (file)
@@ -96,7 +96,23 @@ struct uuid_command {
 
 #ifndef CPU_SUBTYPE_X86_64_H
        #define CPU_SUBTYPE_X86_64_H            ((cpu_subtype_t) 8) 
-#endif 
+#endif
+
+
+#define DYLD_CACHE_ADJ_V2_FORMAT                               0x7F
+
+#define DYLD_CACHE_ADJ_V2_POINTER_32                   0x01
+#define DYLD_CACHE_ADJ_V2_POINTER_64                   0x02
+#define DYLD_CACHE_ADJ_V2_DELTA_32                         0x03
+#define DYLD_CACHE_ADJ_V2_DELTA_64                         0x04
+#define DYLD_CACHE_ADJ_V2_ARM64_ADRP                   0x05
+#define DYLD_CACHE_ADJ_V2_ARM64_OFF12                  0x06
+#define DYLD_CACHE_ADJ_V2_ARM64_BR26                   0x07
+#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT                        0x08
+#define DYLD_CACHE_ADJ_V2_ARM_BR24                             0x09
+#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT              0x0A
+#define DYLD_CACHE_ADJ_V2_THUMB_BR22                   0x0B
+#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32                 0x0C
 
 
 #include "FileAbstraction.hpp"
@@ -801,7 +817,7 @@ public:
                 const macho_segment_command<P>* segcmd = (macho_segment_command<P>*)cmd;
                 if (0 == strncmp(segname, segcmd->segname(), 16)) {
                     return segcmd;
-                }
+              }
             }
             cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
         }
@@ -821,6 +837,8 @@ public:
             }
         }
 
+               if (strcmp(segname, "__DATA") == 0)
+                       return getSection("__DATA_CONST", sectname);
         return NULL;
     }
 
index 4d90a1ef3e8aee43dbd135ba208e131d44dc9c63..c799e307b29b2aaa42b7445e534a99372419961f 100644 (file)
@@ -38,6 +38,7 @@
 #include <unistd.h>
 #include <mach-o/loader.h>
 #include <mach-o/fat.h>
+#include <rootless.h>
 
 #include <vector>
 #include <set>
@@ -69,11 +70,13 @@ public:
        struct Segment
        {
        public:
-                                       Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t align,
-                                                       uint32_t prot, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize),
+                                       Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t sectionsSize,
+                                                       uint64_t sectionsAlignment, uint64_t align,
+                                                       uint32_t prot, uint32_t sectionCount, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize),
                                                        fOrigFileOffset(offset),  fOrigFileSize(file_size), fOrigPermissions(prot), 
                                                        fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align),
-                                                       fPermissions(prot), fNewAddress(0), fMappedAddress(NULL) {
+                                                       fPermissions(prot), fSectionCount(sectionCount), fSectionsSize(sectionsSize),
+                                                       fSectionsAlignment(sectionsAlignment), fNewAddress(0), fMappedAddress(NULL) {
                                                                strlcpy(fOrigName, segName, 16);
                                                        }
                                                        
@@ -88,6 +91,9 @@ public:
                uint64_t        alignment() const       { return fAlignment; }
                const char* name() const                { return fOrigName; }
                uint64_t        newAddress() const      { return fNewAddress; }
+               uint32_t        sectionCount() const{ return fSectionCount; }
+               uint64_t        sectionsSize() const{ return fSectionsSize; }
+               uint64_t        sectionsAlignment() const { return fSectionsAlignment; }
                void*           mappedAddress() const                   { return fMappedAddress; }
                void            setNewAddress(uint64_t addr)    { fNewAddress = addr; }
                void            setMappedAddress(void* addr)    { fMappedAddress = addr; }
@@ -95,6 +101,7 @@ public:
                void            setFileOffset(uint64_t new_off) { fFileOffset = new_off; }
                void            setFileSize(uint64_t new_size)  { fFileSize = new_size; }
                void            setWritable(bool w)                             { if (w) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; }
+               void            setSectionsAlignment(uint64_t v){ fSectionsAlignment = v; }
                void            reset()                                                 { fSize=fOrigSize; fFileOffset=fOrigFileOffset; fFileSize=fOrigFileSize; fPermissions=fOrigPermissions; }
        private:
                uint64_t                fOrigAddress;
@@ -108,6 +115,9 @@ public:
                uint64_t                fFileSize;
                uint64_t                fAlignment;
                uint32_t                fPermissions;
+               uint32_t                fSectionCount;
+               uint64_t                fSectionsSize;
+               uint64_t                fSectionsAlignment;
                uint64_t                fNewAddress;
                void*                   fMappedAddress;
        };
@@ -130,7 +140,8 @@ public:
        virtual bool                                                            isDylib() const = 0;
        virtual bool                                                            isSplitSeg() const = 0;
        virtual bool                                                            hasSplitSegInfo() const = 0;
-       virtual bool                                                            isRootOwned() const = 0;
+       virtual bool                                                            hasSplitSegInfoV2() const = 0;
+       virtual int                                                                     notTrusted() const = 0;
        virtual bool                                                            inSharableLocation() const = 0;
        virtual bool                                                            hasDynamicLookupLinkage() const = 0;
        virtual bool                                                            hasMainExecutableLookupLinkage() const = 0;
@@ -177,8 +188,9 @@ public:
        virtual Library                                                         getID() const                   { return fDylibID; }
        virtual bool                                                            isDylib() const                 { return fIsDylib; }
        virtual bool                                                            isSplitSeg() const;
-       virtual bool                                                            hasSplitSegInfo() const { return fHasSplitSegInfo; }
-       virtual bool                                                            isRootOwned() const             { return fRootOwned; }
+       virtual bool                                                            hasSplitSegInfo() const { return fSplitSegInfo != NULL; }
+       virtual bool                                                            hasSplitSegInfoV2() const{ return fHasSplitSegInfoV2; }
+       virtual int                                                                     notTrusted() const              { return fRootlessErrno; }
        virtual bool                                                            inSharableLocation() const { return fShareableLocation; }
        virtual bool                                                            hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; }
        virtual bool                                                            hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; }
@@ -212,6 +224,9 @@ private:
        uint64_t                                                                        segmentSize(const macho_segment_command<typename A::P>* segCmd) const;
        uint64_t                                                                        segmentFileSize(const macho_segment_command<typename A::P>* segCmd) const;
        uint64_t                                                                        segmentAlignment(const macho_segment_command<typename A::P>* segCmd) const;
+       uint64_t                                                                        sectionsSize(const macho_segment_command<typename A::P>* segCmd) const;
+       uint64_t                                                                        sectionsAlignment(const macho_segment_command<typename A::P>* segCmd) const;
+
        bool                                                                            validReadWriteSeg(const Segment& seg) const;
        
        static cpu_type_t                                                       arch();
@@ -235,8 +250,9 @@ private:
        uint64_t                                                                        fVMExecutableSize;
        uint64_t                                                                        fVMWritablSize;
        uint64_t                                                                        fVMReadOnlySize;
-       bool                                                                            fHasSplitSegInfo;
-       bool                                                                            fRootOwned;
+       const macho_linkedit_data_command<P>*           fSplitSegInfo;
+       bool                                                                            fHasSplitSegInfoV2;
+       int                                                                                     fRootlessErrno;
        bool                                                                            fShareableLocation;
        bool                                                                            fDynamicLookupLinkage;
        bool                                                                            fMainExecutableLookupLinkage;
@@ -506,10 +522,41 @@ uint64_t MachOLayout<A>::segmentFileSize(const macho_segment_command<typename A:
        return segCmd->filesize();
 }
 
+template <typename A>
+uint64_t MachOLayout<A>::sectionsSize(const macho_segment_command<typename A::P>* segCmd) const
+{
+       if ( segCmd->nsects() > 0 ) {
+               const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+               const macho_section<P>* const lastSection = &sectionsStart[segCmd->nsects()-1];
+               uint64_t endSectAddr = lastSection->addr() + lastSection->size();
+               if ( endSectAddr < (segCmd->vmaddr() + segCmd->vmsize()) ) {
+                       uint64_t size =  endSectAddr - segCmd->vmaddr();
+                       return size;
+               }
+       }
+       return segCmd->vmsize();
+}
+
+template <typename A>
+uint64_t MachOLayout<A>::sectionsAlignment(const macho_segment_command<typename A::P>* segCmd) const
+{
+       int p2align = 4;
+       if ( hasSplitSegInfoV2() && (segCmd->nsects() > 0) ) {
+               const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
+               const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()-1];
+               for (const macho_section<P>*  sect=sectionsStart; sect < sectionsEnd; ++sect) {
+                       if ( sect->align() > p2align )
+                               p2align = sect->align();
+               }
+       }
+       return (1 << p2align);
+}
+
+
 
 template <typename A>
 MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid)
- : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0),
+ : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fSplitSegInfo(NULL), fHasSplitSegInfoV2(false), fRootlessErrno(0),
    fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false), 
        fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL)
 {
@@ -537,7 +584,9 @@ MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char*
        fFileType = mh->filetype();
        fArchPair.arch = mh->cputype();
        fArchPair.subtype = mh->cpusubtype();
-       
+       if ( rootless_check_trusted(path) != 0 && rootless_protected_volume(path) == 1)
+               fRootlessErrno = errno;
+
        const macho_dyld_info_command<P>* dyldInfo = NULL;
        const macho_symtab_command<P>* symbolTableCmd = NULL;
        const macho_dysymtab_command<P>* dynamicSymbolTableCmd = NULL;
@@ -571,13 +620,15 @@ MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char*
                                }
                                break;
                        case LC_SEGMENT_SPLIT_INFO:
-                               fHasSplitSegInfo = true;
+                               fSplitSegInfo = (macho_linkedit_data_command<P>*)cmd;
                                break;
                        case macho_segment_command<P>::CMD:
                                {
                                        const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
                                        fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(), 
-                                                               segmentFileSize(segCmd), segmentAlignment(segCmd), segCmd->initprot(), segCmd->segname()));
+                                                               segmentFileSize(segCmd), sectionsSize(segCmd), sectionsAlignment(segCmd),
+                                                               segmentAlignment(segCmd), segCmd->initprot(),
+                                                               segCmd->nsects(), segCmd->segname()));
                                }
                                break;
                        case LC_SYMTAB:
@@ -658,6 +709,18 @@ MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char*
                }
        }
 
+       if ( fSplitSegInfo != NULL ) {
+               const uint8_t* infoStart = (uint8_t*)machHeader + fSplitSegInfo->dataoff();
+               fHasSplitSegInfoV2 = ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT );
+               if ( !fHasSplitSegInfoV2 ) {
+                       // split seg version not known when segments created
+                       // v1 does not support packing, so simulate that by forcing alignment
+                       for (Segment& seg : fSegments) {
+                               seg.setSectionsAlignment(4096);
+                       }
+               }
+       }
+
 }
 
 template <> cpu_type_t MachOLayout<x86>::arch()     { return CPU_TYPE_I386; }
index ac80cd07e314f6f9563ee86cf6c4ba93d24d2e22..21a46342fa6aa67067b94aab022d76ae267f8422 100644 (file)
@@ -56,7 +56,7 @@ public:
        virtual cpu_type_t                                                      getArchitecture() const = 0;
        virtual uint64_t                                                        getBaseAddress() const = 0;
        virtual uint64_t                                                        getVMSize() const = 0;
-       virtual void                                                            rebase(std::vector<void*>&) = 0;
+       virtual bool                                                            rebase(std::vector<void*>&) = 0;
 };
 
 
@@ -70,7 +70,7 @@ public:
        virtual cpu_type_t                                                      getArchitecture() const;
        virtual uint64_t                                                        getBaseAddress() const;
        virtual uint64_t                                                        getVMSize() const;
-       virtual void                                                            rebase(std::vector<void*>&);
+       virtual bool                                                            rebase(std::vector<void*>&);
 
 protected:
        typedef typename A::P                                   P;
@@ -79,24 +79,21 @@ protected:
                
        pint_t*                                                                         mappedAddressForNewAddress(pint_t vmaddress);
        pint_t                                                                          getSlideForNewAddress(pint_t newAddress);
-       
+
 private:
-       void                                                                            calculateRelocBase();
        void                                                                            adjustLoadCommands();
        void                                                                            adjustSymbolTable();
-       void                                                                            optimzeStubs();
-       void                                                                            makeNoPicStub(uint8_t* stub, pint_t logicalAddress);
        void                                                                            adjustDATA();
        void                                                                            adjustCode();
        void                                                                            applyRebaseInfo(std::vector<void*>& pointersInData);
-       void                                                                            adjustExportInfo();
+       void                                                                            adjustReferencesUsingInfoV2(std::vector<void*>& pointersInData);
+       void                                                                            adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide,
+                                                                                                                               uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersInData);
+       bool                                                                            adjustExportInfo();
        void                                                                            doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersInData);
-       void                                                                            adjustSegmentLoadCommand(macho_segment_command<P>* seg);
        pint_t                                                                          getSlideForVMAddress(pint_t vmaddress);
        pint_t                                                                          maskedVMAddress(pint_t vmaddress);
        pint_t*                                                                         mappedAddressForVMAddress(pint_t vmaddress);
-       pint_t*                                                                         mappedAddressForRelocAddress(pint_t r_address);
-       void                                                                            adjustRelocBaseAddresses();
        const uint8_t*                                                          doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta);
        void                                                                            doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta);
        void                                                                            doLocalRelocation(const macho_relocation_info<P>* reloc);
@@ -107,23 +104,20 @@ protected:
        uint8_t*                                                                        fLinkEditBase;                          // add file offset to this to get linkedit content
        const MachOLayoutAbstraction&                           fLayout;
 private:
-       pint_t                                                                          fOrignalVMRelocBaseAddress; // add reloc address to this to get original address reloc referred to
        const macho_symtab_command<P>*                          fSymbolTable;
        const macho_dysymtab_command<P>*                        fDynamicSymbolTable;
        const macho_dyld_info_command<P>*                       fDyldInfo;
+       const macho_linkedit_data_command<P>*           fSplitSegInfo;
        bool                                                                            fSplittingSegments;
-       bool                                                                            fOrignalVMRelocBaseAddressValid;
-       pint_t                                                                          fSkipSplitSegInfoStart;
-       pint_t                                                                          fSkipSplitSegInfoEnd;
+       bool                                                                            fHasSplitSegInfoV2;
+       std::vector<uint64_t>                                           fSectionOffsetsInSegment;
 };
 
 
-
 template <typename A>
 Rebaser<A>::Rebaser(const MachOLayoutAbstraction& layout)
- :     fLayout(layout), fOrignalVMRelocBaseAddress(0), fLinkEditBase(0), 
-       fSymbolTable(NULL), fDynamicSymbolTable(NULL), fDyldInfo(NULL), fSplittingSegments(false), 
-       fOrignalVMRelocBaseAddressValid(false), fSkipSplitSegInfoStart(0), fSkipSplitSegInfoEnd(0)
+ :     fLayout(layout), fLinkEditBase(0), fSymbolTable(NULL), fDynamicSymbolTable(NULL), 
+    fDyldInfo(NULL), fSplitSegInfo(NULL), fSplittingSegments(false), fHasSplitSegInfoV2(false)
 {
        fHeader = (const macho_header<P>*)fLayout.getSegments()[0].mappedAddress();
        switch ( fHeader->filetype() ) {
@@ -161,13 +155,31 @@ Rebaser<A>::Rebaser(const MachOLayoutAbstraction& layout)
                        case LC_DYLD_INFO_ONLY:
                                fDyldInfo = (macho_dyld_info_command<P>*)cmd;
                                break;
+                       case LC_SEGMENT_SPLIT_INFO:
+                               fSplitSegInfo = (macho_linkedit_data_command<P>*)cmd;
+                               break;
+                       case macho_segment_command<P>::CMD: {
+                               // update segment/section file offsets
+                               macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
+                               macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+                               macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
+                               for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+                                       fSectionOffsetsInSegment.push_back(sect->addr() - segCmd->vmaddr());
+                               }
+                       }
                }
                cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
        }       
 
-       calculateRelocBase();
+       if ( fDyldInfo == NULL )
+               throw "no LC_DYLD_INFO load command";
        
        fSplittingSegments = layout.hasSplitSegInfo() && this->unequalSlides();
+
+       if ( fSplitSegInfo != NULL ) {
+               const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()];
+               fHasSplitSegInfoV2 = ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT );
+       }
 }
 
 template <> cpu_type_t Rebaser<x86>::getArchitecture()    const { return CPU_TYPE_I386; }
@@ -210,48 +222,30 @@ uint64_t Rebaser<A>::getVMSize() const
 
 
 template <typename A>
-void Rebaser<A>::rebase(std::vector<void*>& pointersInData)
-{              
-       // update writable segments that have internal pointers
-       if ( fDyldInfo != NULL )
+bool Rebaser<A>::rebase(std::vector<void*>& pointersInData)
+{
+       if ( fHasSplitSegInfoV2 )  {
+               this->adjustReferencesUsingInfoV2(pointersInData);
+       }
+       else {
+               //fprintf(stderr, "warning: dylib with old split-seg info: %s\n", fLayout.getFilePath());
+               // update writable segments that have internal pointers
                this->applyRebaseInfo(pointersInData);
-       else
-               this->adjustDATA();
 
-       // if splitting segments, update code-to-data references
-       this->adjustCode();
-       
-       // change address on relocs now that segments are split
-       this->adjustRelocBaseAddresses();
-       
+               // if splitting segments, update code-to-data references
+               this->adjustCode();
+       }
+
        // update load commands
        this->adjustLoadCommands();
-       
+
        // update symbol table  
        this->adjustSymbolTable();
-       
-       // optimize stubs 
-       this->optimzeStubs();
-       
-       // update export info
-       if ( fDyldInfo != NULL )
-               this->adjustExportInfo();
-}
-
-template <>
-void Rebaser<x86>::adjustSegmentLoadCommand(macho_segment_command<P>* seg)
-{
-       // __IMPORT segments are not-writable in shared cache
-       if ( strcmp(seg->segname(), "__IMPORT") == 0 ) 
-               seg->set_initprot(VM_PROT_READ|VM_PROT_EXECUTE);
-}
 
-template <typename A>
-void Rebaser<A>::adjustSegmentLoadCommand(macho_segment_command<P>* seg)
-{
+       // update export info
+       return this->adjustExportInfo();
 }
 
-
 template <typename A>
 void Rebaser<A>::adjustLoadCommands()
 {
@@ -288,7 +282,6 @@ void Rebaser<A>::adjustLoadCommands()
                                // update segment commands
                                {
                                        macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                                       this->adjustSegmentLoadCommand(seg);
                                        pint_t slide = this->getSlideForVMAddress(seg->vmaddr());
                                        seg->set_vmaddr(seg->vmaddr() + slide);
                                        macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
@@ -371,76 +364,6 @@ typename A::P::uint_t Rebaser<A>::getSlideForNewAddress(pint_t newAddress)
        throwf("new address 0x%08llX not found", (uint64_t)newAddress);
 }
 
-template <typename A>
-typename A::P::uint_t* Rebaser<A>::mappedAddressForRelocAddress(pint_t r_address)
-{
-       if ( fOrignalVMRelocBaseAddressValid )
-               return this->mappedAddressForVMAddress(r_address + fOrignalVMRelocBaseAddress);
-       else
-               throw "can't apply relocation.  Relocation base not known";
-}
-
-
-template <>
-void Rebaser<arm>::makeNoPicStub(uint8_t* stub, pint_t logicalAddress) 
-{
-       uint32_t* instructions = (uint32_t*)stub;
-       if ( (LittleEndian::get32(instructions[0]) == 0xE59FC004) && 
-               (LittleEndian::get32(instructions[1]) == 0xE08FC00C) && 
-               (LittleEndian::get32(instructions[2]) == 0xE59CF000) ) {
-                       uint32_t lazyPtrAddress = instructions[3] + logicalAddress + 12;
-                       LittleEndian::set32(instructions[0], 0xE59FC000);               //      ldr ip, [pc, #0]
-                       LittleEndian::set32(instructions[1], 0xE59CF000);               //      ldr pc, [ip]
-                       LittleEndian::set32(instructions[2], lazyPtrAddress);   //      .long   L_foo$lazy_ptr
-                       LittleEndian::set32(instructions[3], 0xE1A00000);               //      nop
-       }
-       else
-               fprintf(stderr, "unoptimized stub in %s at 0x%08X\n", fLayout.getFilePath(), logicalAddress);
-}
-
-
-#if 0 
-// disable this optimization do allow cache to slide
-template <>
-void Rebaser<arm>::optimzeStubs()
-{
-       // convert pic stubs to no-pic stubs in dyld shared cache
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                       macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                       macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
-                       macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                       for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
-                               if ( (sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS ) {
-                                       const uint32_t stubSize = sect->reserved2();
-                                       // ARM PIC stubs are 4 32-bit instructions long
-                                       if ( stubSize == 16 ) {
-                                               uint32_t stubCount = sect->size() / 16;
-                                               pint_t stubLogicalAddress = sect->addr();
-                                               uint8_t* stubMappedAddress = (uint8_t*)mappedAddressForNewAddress(stubLogicalAddress);
-                                               for(uint32_t s=0; s < stubCount; ++s) {
-                                                       makeNoPicStub(stubMappedAddress, stubLogicalAddress);
-                                                       stubLogicalAddress += 16;
-                                                       stubMappedAddress += 16;
-                                               }
-                                       }
-                               }
-                       }
-               }
-               cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }
-}
-#endif
-
-template <typename A>
-void Rebaser<A>::optimzeStubs()
-{
-       // other architectures don't need stubs changed in shared cache
-}
-
 template <typename A>
 void Rebaser<A>::adjustSymbolTable()
 {
@@ -462,15 +385,15 @@ void Rebaser<A>::adjustSymbolTable()
 }
 
 template <typename A>
-void Rebaser<A>::adjustExportInfo()
+bool Rebaser<A>::adjustExportInfo()
 {
        // if no export info, nothing to adjust
        if ( fDyldInfo->export_size() == 0 )
-               return;
+               return true;
 
        // since export info addresses are offsets from mach_header, everything in __TEXT is fine
        // only __DATA addresses need to be updated
-       const uint8_t* start = fLayout.getDyldInfoExports(); 
+       const uint8_t* start = fLayout.getDyldInfoExports();
        const uint8_t* end = &start[fDyldInfo->export_size()];
        std::vector<mach_o::trie::Entry> originalExports;
        try {
@@ -507,13 +430,24 @@ void Rebaser<A>::adjustExportInfo()
        while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 )
                newExportTrieBytes.push_back(0);
        
-       // allocate new buffer and set export_off to use new buffer instead
        uint32_t newExportsSize = newExportTrieBytes.size();
-       uint8_t* sideTrie = new uint8_t[newExportsSize];
-       memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize);
-       fLayout.setDyldInfoExports(sideTrie);
-       ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie
-       ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
+       if ( newExportsSize <= fDyldInfo->export_size() ) {
+               // override existing trie in place
+               uint8_t *realStart = &fLinkEditBase[fDyldInfo->export_off()];
+               bzero(realStart, fDyldInfo->export_size());
+               memcpy(realStart, &newExportTrieBytes[0], newExportsSize);
+               fLayout.setDyldInfoExports(realStart);
+               return true;
+       }
+       else {
+               // allocate new buffer and set export_off in layout object to use new buffer instead
+               uint8_t* sideTrie = new uint8_t[newExportsSize];
+               memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize);
+               fLayout.setDyldInfoExports(sideTrie);
+               ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie
+               ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
+               return false;
+       }
 }
 
 
@@ -521,15 +455,6 @@ void Rebaser<A>::adjustExportInfo()
 template <typename A>
 void Rebaser<A>::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta)
 {
-       // begin hack for <rdar://problem/8253549> split seg info wrong for x86_64 stub helpers
-       if ( (fSkipSplitSegInfoStart <= address) && (address < fSkipSplitSegInfoEnd) ) {
-               uint8_t* p = (uint8_t*)mappedAddressForVMAddress(address);
-               // only ignore split seg info for "push" instructions
-               if ( p[-1] == 0x68 )
-                       return;
-       }
-       // end hack for <rdar://problem/8253549> 
-       
        //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX, path=%s)\n", 
        //                              kind, address, codeToDataDelta, codeToImportDelta, fLayout.getFilePath());
        uint32_t* p;
@@ -692,39 +617,8 @@ void Rebaser<A>::adjustCode()
 {
        if ( fSplittingSegments ) {
                // get uleb128 compressed runs of code addresses to update
-               const uint8_t* infoStart = NULL;
-               const uint8_t* infoEnd = NULL;
-               const macho_segment_command<P>* seg;
-               const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
-               const uint32_t cmd_count = fHeader->ncmds();
-               const macho_load_command<P>* cmd = cmds;
-               for (uint32_t i = 0; i < cmd_count; ++i) {
-                       switch (cmd->cmd()) {
-                               case LC_SEGMENT_SPLIT_INFO:
-                                       {
-                                               const macho_linkedit_data_command<P>* segInfo = (macho_linkedit_data_command<P>*)cmd;
-                                               infoStart = &fLinkEditBase[segInfo->dataoff()];
-                                               infoEnd = &infoStart[segInfo->datasize()];
-                                       }
-                                       break;
-                               // begin hack for <rdar://problem/8253549> split seg info wrong for x86_64 stub helpers
-                               case macho_segment_command<P>::CMD:
-                                       seg = (macho_segment_command<P>*)cmd;
-                                       if ( (getArchitecture() == CPU_TYPE_X86_64) && (strcmp(seg->segname(), "__TEXT") == 0) ) {
-                                               const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
-                                               const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                                               for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
-                                                       if ( strcmp(sect->sectname(), "__stub_helper") == 0 ) {
-                                                               fSkipSplitSegInfoStart = sect->addr();
-                                                               fSkipSplitSegInfoEnd = sect->addr() + sect->size() - 16;
-                                                       }       
-                                               }
-                                       }
-                                       break;
-                               // end hack for <rdar://problem/8253549> split seg info wrong for x86_64 stub helpers
-                       }
-                       cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-               }
+               const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()];
+               const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];;
                // calculate how much we need to slide writable segments
                const uint64_t orgBaseAddress = this->getBaseAddress();
                int64_t codeToDataDelta = 0;
@@ -733,9 +627,7 @@ void Rebaser<A>::adjustCode()
                const MachOLayoutAbstraction::Segment& codeSeg = segments[0];
                for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
                        const MachOLayoutAbstraction::Segment& dataSeg = *it;
-                       if ( strcmp(dataSeg.name(), "__IMPORT") == 0 )
-                               codeToImportDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address());
-                       else if ( dataSeg.writable() ) {
+                       if ( dataSeg.writable() ) {
                                if ( (strcmp(dataSeg.name(), "__DATA") != 0) && (strcmp(dataSeg.name(), "__OBJC") != 0) )
                                        throwf("only one rw segment named '__DATA' can be used in dylibs placed in the dyld shared cache (%s)", fLayout.getFilePath());
                                codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address());
@@ -860,262 +752,368 @@ void Rebaser<A>::applyRebaseInfo(std::vector<void*>& pointersInData)
        }       
 }
 
-template <typename A>
-void Rebaser<A>::adjustDATA()
+template <>
+void Rebaser<arm64>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide,
+                                                                       uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersInData)
 {
-       // walk all local relocations and slide every pointer
-       const macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[fDynamicSymbolTable->locreloff()]);
-       const macho_relocation_info<P>* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()];
-       for (const macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
-               this->doLocalRelocation(reloc);
-       }
-       
-       // walk non-lazy-pointers and slide the ones that are LOCAL
-       const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
-       const uint32_t cmd_count = fHeader->ncmds();
-       const macho_load_command<P>* cmd = cmds;
-       for (uint32_t i = 0; i < cmd_count; ++i) {
-               if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                       const macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
-                       const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
-                       const macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
-                       const uint32_t* const indirectTable = (uint32_t*)(&fLinkEditBase[fDynamicSymbolTable->indirectsymoff()]);
-                       for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
-                               if ( (sect->flags() & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS ) {
-                                       const uint32_t indirectTableOffset = sect->reserved1();
-                                       uint32_t pointerCount = sect->size() / sizeof(pint_t);
-                                       pint_t* nonLazyPointerAddr = this->mappedAddressForVMAddress(sect->addr());
-                                       for (uint32_t j=0; j < pointerCount; ++j, ++nonLazyPointerAddr) {
-                                               if ( E::get32(indirectTable[indirectTableOffset + j]) == INDIRECT_SYMBOL_LOCAL ) {
-                                                       pint_t value = A::P::getP(*nonLazyPointerAddr);
-                                                       P::setP(*nonLazyPointerAddr, value + this->getSlideForVMAddress(value));
-                                               }
+       uint64_t value64;
+       uint64_t* mappedAddr64;
+       uint32_t value32;
+       uint32_t* mappedAddr32;
+       uint32_t instruction;
+       int64_t offsetAdjust;
+       switch ( kind ) {
+               case DYLD_CACHE_ADJ_V2_DELTA_32:
+                       mappedAddr32 = (uint32_t*)mappedAddr;
+                       value32 = arm64::P::E::get32(*mappedAddr32);
+                       E::set32(*mappedAddr32, value32 + adjust);
+                       break;
+               case DYLD_CACHE_ADJ_V2_POINTER_64:
+                       mappedAddr64 = (uint64_t*)mappedAddr;
+                       if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) )
+                               throwf("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX\n", fromNewAddress);
+                       E::set64(*mappedAddr64, toNewAddress);
+                       pointersInData.push_back(mappedAddr);
+                       break;
+               case DYLD_CACHE_ADJ_V2_DELTA_64:
+                       mappedAddr64 = (uint64_t*)mappedAddr;
+                       value64 = arm64::P::E::get64(*mappedAddr64);
+                       E::set64(*mappedAddr64, value64 + adjust);
+                       break;
+               case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
+                       mappedAddr32 = (uint32_t*)mappedAddr;
+                       value64 = toNewAddress - imageStartAddress;
+                       E::set32(*mappedAddr32, (uint32_t)value64);
+                       break;
+               case DYLD_CACHE_ADJ_V2_ARM64_ADRP:
+                       mappedAddr32 = (uint32_t*)mappedAddr;
+                       instruction = arm64::P::E::get32(*mappedAddr32);
+                       if ( (instruction & 0x9F000000) == 0x90000000 ) {
+                               //value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9);
+                               uint32_t newPage21 = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF)) >> 12;
+                               instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0);
+                               arm64::P::E::set32(*mappedAddr32, instruction);
+                       }
+                       else {
+                               // ADRP instructions are sometimes optimized to other instructions (e.g. ADR) after the split-seg-info is generated
+                       }
+                       break;
+               case DYLD_CACHE_ADJ_V2_ARM64_OFF12:
+                       mappedAddr32 = (uint32_t*)mappedAddr;
+                       instruction = arm64::P::E::get32(*mappedAddr32);
+                       offsetAdjust = (adjust & 0xFFF);
+                       if ( offsetAdjust == 0 )
+                               break;
+                       if ( (instruction & 0x3B000000) == 0x39000000 ) {
+                               // LDR/STR imm12
+                               if ( offsetAdjust != 0 ) {
+                                       uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
+                                       uint32_t newAddend;
+                                       switch ( instruction & 0xC0000000 ) {
+                                               case 0x00000000:
+                                                       if ( (instruction & 0x04800000) == 0x04800000 ) {
+                                                               if ( offsetAdjust & 0xF )
+                                                                       throwf("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr);
+                                                               if ( encodedAddend*16 >= 4096 )
+                                                                       throwf("off12 scale=16 instruction points outside its page at mapped address=%p", mappedAddr);
+                                                               newAddend = (encodedAddend + offsetAdjust/16) % 256;
+                                                       }
+                                                       else {
+                                                               // scale=1
+                                                               newAddend = encodedAddend + offsetAdjust;
+                                                       }
+                                                       break;
+                                               case 0x40000000:
+                                                       if ( offsetAdjust & 1 )
+                                                               throwf("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr);
+                                                       if ( encodedAddend*2 >= 4096 )
+                                                               throwf("off12 scale=2 instruction points outside its page at mapped address=%p", mappedAddr);
+                                                       newAddend = (encodedAddend + offsetAdjust/2) % 2048;
+                                                       break;
+                                               case 0x80000000:
+                                                       if ( offsetAdjust & 3 )
+                                                               throwf("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr);
+                                                       if ( encodedAddend*4 >= 4096 )
+                                                               throwf("off12 scale=4 instruction points outside its page at mapped address=%p", mappedAddr);
+                                                       newAddend = (encodedAddend + offsetAdjust/4) % 1024;
+                                                       break;
+                                               case 0xC0000000:
+                                                       if ( offsetAdjust & 7 )
+                                                               throwf("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p", offsetAdjust, mappedAddr);
+                                                       if ( encodedAddend*8 >= 4096 )
+                                                               throwf("off12 scale=8 instruction points outside its page at mapped address=%p", mappedAddr);
+                                                       newAddend = (encodedAddend + offsetAdjust/8) % 512;
+                                                       break;
                                        }
+                                       uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
+                                       arm64::P::E::set32(*mappedAddr32, newInstruction);
                                }
                        }
-               }
-               cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-       }       
-}
-
-
-template <typename A>
-void Rebaser<A>::adjustRelocBaseAddresses()
-{
-       // split seg file need reloc base to be first writable segment
-       if ( fSplittingSegments && ((fHeader->flags() & MH_SPLIT_SEGS) == 0) ) {
-
-               // get amount to adjust reloc address
-               int32_t relocAddressAdjust = 0;
-               const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-               for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
-                       const MachOLayoutAbstraction::Segment& seg = *it;
-                       if ( seg.writable() ) {
-                               relocAddressAdjust = seg.address() - segments[0].address();
-                               break;
+                       else if ( (instruction & 0xFFC00000) == 0x91000000 ) {
+                               // ADD imm12
+                               if ( instruction & 0x00C00000 )
+                                       throwf("ADD off12 uses shift at mapped address=%p", mappedAddr);
+                               uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
+                               uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF;
+                               uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
+                               arm64::P::E::set32(*mappedAddr32, newInstruction);
                        }
-               }
-
-               // walk all local relocations and adjust every address 
-               macho_relocation_info<P>* const relocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[fDynamicSymbolTable->locreloff()]);
-               macho_relocation_info<P>* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()];
-               for (macho_relocation_info<P>* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
-                       reloc->set_r_address(reloc->r_address()-relocAddressAdjust);
-               }
-               
-               // walk all external relocations and adjust every address 
-               macho_relocation_info<P>* const externRelocsStart = (macho_relocation_info<P>*)(&fLinkEditBase[fDynamicSymbolTable->extreloff()]);
-               macho_relocation_info<P>* const externRelocsEnd = &externRelocsStart[fDynamicSymbolTable->nextrel()];
-               for (macho_relocation_info<P>* reloc=externRelocsStart; reloc < externRelocsEnd; ++reloc) {
-                       reloc->set_r_address(reloc->r_address()-relocAddressAdjust);
-               }
+                       else if ( instruction != 0xD503201F ) {
+                               // ignore imm12 instructions optimized into a NOP, but warn about others
+                               fprintf(stderr, "unknown off12 instruction 0x%08X at 0x%0llX in %s\n", instruction, fromNewAddress, fLayout.getFilePath());
+                       }
+                       break;
+               case DYLD_CACHE_ADJ_V2_ARM64_BR26:
+                       // nothing to do with calls to stubs
+                       break;
+               default:
+                       throwf("unknown split seg kind=%d", kind);
        }
 }
 
-template <>
-void Rebaser<x86_64>::adjustRelocBaseAddresses()
+static bool isThumbMovw(uint32_t instruction)
 {
-       // x86_64 already have reloc base of first writable segment
+       return ( (instruction & 0x8000FBF0) == 0x0000F240 );
 }
 
-
-template <>
-void Rebaser<x86_64>::doLocalRelocation(const macho_relocation_info<x86_64::P>* reloc)
+static bool isThumbMovt(uint32_t instruction)
 {
-       if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) {
-               pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
-               pint_t value = P::getP(*addr);
-               P::setP(*addr, value + this->getSlideForVMAddress(value));
-       }
-       else {
-               throw "invalid relocation type";
-       }
+       return ( (instruction & 0x8000FBF0) == 0x0000F2C0 );
 }
 
-template <>
-void Rebaser<x86>::doLocalRelocation(const macho_relocation_info<P>* reloc)
+static uint16_t getThumbWord(uint32_t instruction)
 {
-       if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
-               if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
-                       pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
-                       pint_t value = P::getP(*addr);
-                       P::setP(*addr, value + this->getSlideForVMAddress(value));
-               }
-       }
-       else {
-               macho_scattered_relocation_info<P>* sreloc = (macho_scattered_relocation_info<P>*)reloc;
-               if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) {
-                       sreloc->set_r_value( sreloc->r_value() + this->getSlideForVMAddress(sreloc->r_value()) );
-               }
-               else {
-                       throw "cannot rebase final linked image with scattered relocations";
-               }
-       }
+       uint32_t i = ((instruction & 0x00000400) >> 10);
+       uint32_t imm4 = (instruction & 0x0000000F);
+       uint32_t imm3 = ((instruction & 0x70000000) >> 28);
+       uint32_t imm8 = ((instruction & 0x00FF0000) >> 16);
+       return ((imm4 << 12) | (i << 11) | (imm3 << 8) | imm8);
 }
 
-template <typename A>
-void Rebaser<A>::doLocalRelocation(const macho_relocation_info<P>* reloc)
-{
-       if ( (reloc->r_address() & R_SCATTERED) == 0 ) {
-               if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) {
-                       pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address());
-                       pint_t value = P::getP(*addr);
-                       P::setP(*addr, value + this->getSlideForVMAddress(value));
-               }
-       }
-       else {
-               throw "cannot rebase final linked image with scattered relocations";
-       }
+static uint32_t setThumbWord(uint32_t instruction, uint16_t word) {
+       uint32_t imm4 = (word & 0xF000) >> 12;
+       uint32_t i =    (word & 0x0800) >> 11;
+       uint32_t imm3 = (word & 0x0700) >> 8;
+       uint32_t imm8 =  word & 0x00FF;
+       return (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16);
 }
 
-
-template <typename A>
-void Rebaser<A>::calculateRelocBase()
+static bool isArmMovw(uint32_t instruction)
 {
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       if ( fHeader->flags() & MH_SPLIT_SEGS ) {
-               // reloc addresses are from the start of the first writable segment
-               for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
-                       const MachOLayoutAbstraction::Segment& seg = *it;
-                       if ( seg.writable() ) {
-                               // found first writable segment
-                               fOrignalVMRelocBaseAddress = seg.address();
-                               fOrignalVMRelocBaseAddressValid = true;
-                       }
-               }
-       }
-       else {
-               // reloc addresses are from the start of the mapped file (base address)
-               fOrignalVMRelocBaseAddress = segments[0].address();
-               fOrignalVMRelocBaseAddressValid = true;
-       }
+       return (instruction & 0x0FF00000) == 0x03000000;
 }
 
+static bool isArmMovt(uint32_t instruction)
+{
+       return (instruction & 0x0FF00000) == 0x03400000;
+}
 
-template <>
-void Rebaser<x86_64>::calculateRelocBase()
+static uint16_t getArmWord(uint32_t instruction)
 {
-       // reloc addresses are always based from the start of the first writable segment
-       const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
-       for(std::vector<MachOLayoutAbstraction::Segment>::const_iterator it = segments.begin(); it != segments.end(); ++it) {
-               const MachOLayoutAbstraction::Segment& seg = *it;
-               if ( seg.writable() ) {
-                       // found first writable segment
-                       fOrignalVMRelocBaseAddress = seg.address();
-                       fOrignalVMRelocBaseAddressValid = true;
-               }
-       }
+       uint32_t imm4 = ((instruction & 0x000F0000) >> 16);
+       uint32_t imm12 = (instruction & 0x00000FFF);
+       return (imm4 << 12) | imm12;
+}
+
+static uint32_t setArmWord(uint32_t instruction, uint16_t word) {
+       uint32_t imm4 = (word & 0xF000) >> 12;
+       uint32_t imm12 = word & 0x0FFF;
+       return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12;
 }
 
 
-#if 0
-class MultiArchRebaser
+template <>
+void Rebaser<arm>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide,
+                                                                 uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersInData)
 {
-public:                                
-               MultiArchRebaser::MultiArchRebaser(const char* path, bool writable=false)
-                : fMappingAddress(0), fFileSize(0)
-               {
-                       // map in whole file
-                       int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0);
-                       if ( fd == -1 )
-                               throwf("can't open file, errno=%d", errno);
-                       struct stat stat_buf;
-                       if ( fstat(fd, &stat_buf) == -1)
-                               throwf("can't stat open file %s, errno=%d", path, errno);
-                       if ( stat_buf.st_size < 20 )
-                               throwf("file too small %s", path);
-                       const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ;
-                       const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE);
-                       uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0);
-                       if ( p == (uint8_t*)(-1) )
-                               throwf("can't map file %s, errno=%d", path, errno);
-                       ::close(fd);
-
-                       // if fat file, process each architecture
-                       const fat_header* fh = (fat_header*)p;
-                       const mach_header* mh = (mach_header*)p;
-                       if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
-                               // Fat header is always big-endian
-                               const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
-                               for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
-                                       uint32_t fileOffset = OSSwapBigToHostInt32(archs[i].offset);
-                                       try {
-                                               switch ( OSSwapBigToHostInt32(archs[i].cputype) ) {
-                                                       case CPU_TYPE_I386:
-                                                               fRebasers.push_back(new Rebaser<x86>(&p[fileOffset]));
-                                                               break;
-                                                       case CPU_TYPE_X86_64:
-                                                               fRebasers.push_back(new Rebaser<x86_64>(&p[fileOffset]));
-                                                               break;
-                                                       case CPU_TYPE_ARM:
-                                                               fRebasers.push_back(new Rebaser<arm>(&p[fileOffset]));
-                                                               break;
-                                                       default:
-                                                               throw "unknown file format";
-                                               }
+       uint32_t value32;
+       uint32_t* mappedAddr32 = (uint32_t*)mappedAddr;
+       static uint32_t* lastMappedAddr32 = NULL;
+       static uint32_t lastKind = 0;
+       static uint32_t lastToNewAddress = 0;
+       switch ( kind ) {
+               case DYLD_CACHE_ADJ_V2_DELTA_32:
+                       value32 = arm64::P::E::get32(*mappedAddr32);
+                       E::set32(*mappedAddr32, value32 + adjust);
+                       break;
+               case DYLD_CACHE_ADJ_V2_POINTER_32:
+                       if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) )
+                               throwf("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX\n", fromNewAddress);
+                       E::set32(*mappedAddr32, toNewAddress);
+                       pointersInData.push_back( mappedAddr);
+                       break;
+               case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32:
+                       mappedAddr32 = (uint32_t*)mappedAddr;
+                       value32 = (uint32_t)(toNewAddress - imageStartAddress);
+                       E::set32(*mappedAddr32, value32);
+                       break;
+               case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT:
+                       // to update a movw/movt pair we need to extract the 32-bit they will make,
+                       // add the adjust and write back the new movw/movt pair.
+                       if ( lastKind == kind ) {
+                               if ( lastToNewAddress == toNewAddress ) {
+                                       uint32_t instruction1 = E::get32(*lastMappedAddr32);
+                                       uint32_t instruction2 = E::get32(*mappedAddr32);
+                                       if ( isThumbMovw(instruction1) && isThumbMovt(instruction2) ) {
+                                               uint16_t high = getThumbWord(instruction2);
+                                               uint16_t low  = getThumbWord(instruction1);
+                                               uint32_t full = high << 16 | low;
+                                               full += adjust;
+                                               instruction1 = setThumbWord(instruction1, full & 0xFFFF);
+                                               instruction2 = setThumbWord(instruction2, full >> 16);
+                                       }
+                                       else if ( isThumbMovt(instruction1) && isThumbMovw(instruction2) ) {
+                                               uint16_t high = getThumbWord(instruction1);
+                                               uint16_t low  = getThumbWord(instruction2);
+                                               uint32_t full = high << 16 | low;
+                                               full += adjust;
+                                               instruction2 = setThumbWord(instruction2, full & 0xFFFF);
+                                               instruction1 = setThumbWord(instruction1, full >> 16);
                                        }
-                                       catch (const char* msg) {
-                                               fprintf(stderr, "rebase warning: %s for %s\n", msg, path);
+                                       else {
+                                               throw "two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not";
                                        }
+                                       E::set32(*lastMappedAddr32, instruction1);
+                                       E::set32(*mappedAddr32, instruction2);
+                                       kind = 0;
+                               }
+                               else {
+                                       throw "two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses";
                                }
                        }
-                       else {
-                               try {
-                                       if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
-                                               fRebasers.push_back(new Rebaser<x86>(mh));
-                                       }
-                                       else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
-                                               fRebasers.push_back(new Rebaser<x86_64>(mh));
+                       break;
+               case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT:
+                       // to update a movw/movt pair we need to extract the 32-bit they will make,
+                       // add the adjust and write back the new movw/movt pair.
+                       if ( lastKind == kind ) {
+                               if ( lastToNewAddress == toNewAddress ) {
+                                       uint32_t instruction1 = E::get32(*lastMappedAddr32);
+                                       uint32_t instruction2 = E::get32(*mappedAddr32);
+                                       if ( isArmMovw(instruction1) && isArmMovt(instruction2) ) {
+                                               uint16_t high = getArmWord(instruction2);
+                                               uint16_t low  = getArmWord(instruction1);
+                                               uint32_t full = high << 16 | low;
+                                               full += adjust;
+                                               instruction1 = setArmWord(instruction1, full & 0xFFFF);
+                                               instruction2 = setArmWord(instruction2, full >> 16);
                                        }
-                                       else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
-                                               fRebasers.push_back(new Rebaser<arm>(mh));
+                                       else if ( isArmMovt(instruction1) && isArmMovw(instruction2) ) {
+                                               uint16_t high = getArmWord(instruction1);
+                                               uint16_t low  = getArmWord(instruction2);
+                                               uint32_t full = high << 16 | low;
+                                               full += adjust;
+                                               instruction2 = setArmWord(instruction2, full & 0xFFFF);
+                                               instruction1 = setArmWord(instruction1, full >> 16);
                                        }
                                        else {
-                                               throw "unknown file format";
+                                               throw "two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not";
                                        }
+                                       E::set32(*lastMappedAddr32, instruction1);
+                                       E::set32(*mappedAddr32, instruction2);
+                                       kind = 0;
                                }
-                               catch (const char* msg) {
-                                       fprintf(stderr, "rebase warning: %s for %s\n", msg, path);
+                               else {
+                                       throw "two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses";
                                }
                        }
-                       
-                       fMappingAddress = p;
-                       fFileSize = stat_buf.st_size;
-               }
+                       break;
+               case DYLD_CACHE_ADJ_V2_ARM_BR24:
+               case DYLD_CACHE_ADJ_V2_THUMB_BR22:
+                       // nothing to do with calls to stubs
+                       break;
+               default:
+                       throwf("v2 split seg info kind (%d) not supported yet", kind);
+       }
+       lastKind = kind;
+       lastToNewAddress = toNewAddress;
+       lastMappedAddr32 = mappedAddr32;
+}
 
 
-               ~MultiArchRebaser()     {::munmap(fMappingAddress, fFileSize); }
+template <typename A>
+void Rebaser<A>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide,
+                                                               uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector<void*>& pointersInData)
+{
+       throw "v2 split seg info not supported yet";
+}
 
-       const std::vector<AbstractRebaser*>&            getArchs() const { return fRebasers; }
-       void                                                                            commit()                { ::msync(fMappingAddress, fFileSize, MS_ASYNC);  }
 
-private:
-       std::vector<AbstractRebaser*>                           fRebasers;
-       void*                                                                           fMappingAddress;
-       uint64_t                                                                        fFileSize;
-};
-#endif
+template <typename A>
+void Rebaser<A>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersInData)
+{
+       static const bool log = false;
+
+       const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()];
+       const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];;
+       if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT )
+               throw "malformed split seg info";
+
+       // build section arrays of slide and mapped address for each section
+       std::vector<uint64_t> sectionSlides;
+       std::vector<uint64_t> sectionNewAddress;
+       std::vector<uint8_t*> sectionMappedAddress;
+       sectionSlides.reserve(16);
+       sectionNewAddress.reserve(16);
+       sectionMappedAddress.reserve(16);
+       // section index 0 refers to mach_header
+       const MachOLayoutAbstraction::Segment& textSeg = fLayout.getSegments().front();
+       sectionMappedAddress.push_back((uint8_t*)textSeg.mappedAddress());
+       sectionSlides.push_back(textSeg.newAddress() - textSeg.address());
+       sectionNewAddress.push_back(textSeg.newAddress());
+       // section 1 and later refer to real sections
+       unsigned sectionIndex = 0;
+       for (const MachOLayoutAbstraction::Segment& seg : fLayout.getSegments()) {
+               uint64_t segSlide = seg.newAddress() - seg.address();
+               for (uint32_t i=0; i < seg.sectionCount(); ++i) {
+                       if (log) fprintf(stderr, "seg=%s, sectIndex=%d, mapped at=%p, offsetInSeg=0x%08llX\n", seg.name(), sectionIndex, seg.mappedAddress(), fSectionOffsetsInSegment[sectionIndex]);
+                       sectionMappedAddress.push_back((uint8_t*)seg.mappedAddress() + fSectionOffsetsInSegment[sectionIndex]);
+                       sectionSlides.push_back(segSlide);
+                       sectionNewAddress.push_back(seg.newAddress() + fSectionOffsetsInSegment[sectionIndex]);
+                       ++sectionIndex;
+               }
+       }
 
+       // Whole                 :== <count> FromToSection+
+       // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
+       // ToOffset              :== <to-sect-offset-delta> <count> FromOffset+
+       // FromOffset    :== <kind> <count> <from-sect-offset-delta>
+       const uint8_t* p = infoStart;
+       uint64_t sectionCount = read_uleb128(p, infoEnd);
+       for (uint64_t i=0; i < sectionCount; ++i) {
+               uint64_t fromSectionIndex = read_uleb128(p, infoEnd);
+               uint64_t toSectionIndex = read_uleb128(p, infoEnd);
+               uint64_t toOffsetCount = read_uleb128(p, infoEnd);
+               uint64_t fromSectionSlide = sectionSlides[fromSectionIndex];
+               uint64_t fromSectionNewAddress = sectionNewAddress[fromSectionIndex];
+               uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex];
+               uint64_t toSectionSlide = sectionSlides[toSectionIndex];
+               uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex];
+               if (log) printf("from sect=%lld (mapped=%p), to sect=%lld (new addr=0x%llX):\n", fromSectionIndex, fromSectionMappedAddress, toSectionIndex, toSectionNewAddress);
+               uint64_t toSectionOffset = 0;
+               for (uint64_t j=0; j < toOffsetCount; ++j) {
+                       uint64_t toSectionDelta = read_uleb128(p, infoEnd);
+                       uint64_t fromOffsetCount = read_uleb128(p, infoEnd);
+                       toSectionOffset += toSectionDelta;
+                       for (uint64_t k=0; k < fromOffsetCount; ++k) {
+                               uint64_t kind = read_uleb128(p, infoEnd);
+                               uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
+                               uint64_t fromSectionOffset = 0;
+                               for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
+                                       uint64_t delta = read_uleb128(p, infoEnd);
+                                       fromSectionOffset += delta;
+                                       int64_t deltaAdjust = toSectionSlide - fromSectionSlide;
+                                       if (log) printf("  kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide);
+                                       uint8_t*  fromMappedAddr = fromSectionMappedAddress + fromSectionOffset;
+                                       uint64_t toNewAddress = toSectionNewAddress + toSectionOffset;
+                                       uint64_t fromNewAddress = fromSectionNewAddress + fromSectionOffset;
+                                       adjustReference(kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, sectionNewAddress.front(), sectionNewAddress.back(), pointersInData);
+                               }
+                       }
+               }
+       }
+
+
+}
 
 #endif // __MACHO_REBASER__
 
index 19ee2d3f4bc08295cc6378ee1fbe73044838758c..23018c752942e4a825f31e4cd2f56ff0ceba9906 100644 (file)
@@ -151,6 +151,7 @@ template <typename A, typename V>
 class LegacySelectorUpdater {
 
     typedef typename A::P P;
+    typedef typename A::P::uint_t pint_t;
 
     static void visitMethodList(objc_method_list<A> *mlist, V& visitor)
     {
@@ -224,10 +225,10 @@ public:
 
         // Message refs
         PointerSection<A, const char *> selrefs(cache, header, "__OBJC", "__message_refs");
-        for (uint64_t s = 0; s < selrefs.count(); s++) {
-            uint64_t oldValue = selrefs.getUnmapped(s);
-            uint64_t newValue = visitor.visit(oldValue);
-            selrefs.set(s, newValue);
+        for (pint_t s = 0; s < selrefs.count(); s++) {
+            pint_t oldValue = selrefs.getVMAddress(s);
+            pint_t newValue = visitor.visit(oldValue);
+            selrefs.setVMAddress(s, newValue);
         }
     }
 };
index e41749d60381ce4c4f36e4f2651889859e12bbae..dfa332ab888b7d9811f3c1c2dc90740862302e26 100644 (file)
@@ -96,17 +96,6 @@ struct entsize_iterator {
     bool operator > (const entsize_iterator<A,T,Tlist>& rhs) {
         return this->current > rhs.current;
     }
-
-    
-    static void overwrite(entsize_iterator<A,T,Tlist>& dst, const Tlist* srcList)
-    {
-        entsize_iterator<A,T,Tlist> src;
-        uint32_t ee = srcList->getEntsize();
-        for (src = srcList->begin(); src != srcList->end(); ++src) {
-            memcpy(&*dst, &*src, ee);
-            ++dst;
-        }
-    }
 };
 
 template <typename A> 
@@ -426,18 +415,41 @@ private:
     void* operator new (size_t);
 };
 
+
+template <typename A> class objc_protocol_list_t;  // forward reference
+
 template <typename A>
 class objc_protocol_t {
-    typename A::P::uint_t isa;
-    typename A::P::uint_t name;
-    typename A::P::uint_t protocols;
-    typename A::P::uint_t instanceMethods;
-    typename A::P::uint_t classMethods;
-    typename A::P::uint_t optionalInstanceMethods;
-    typename A::P::uint_t optionalClassMethods;
-    typename A::P::uint_t instanceProperties;
+    typedef typename A::P::uint_t pint_t;
+
+    pint_t isa;
+    pint_t name;
+    pint_t protocols;
+    pint_t instanceMethods;
+    pint_t classMethods;
+    pint_t optionalInstanceMethods;
+    pint_t optionalClassMethods;
+    pint_t instanceProperties;
+    uint32_t size;
+    uint32_t flags;
+    pint_t extendedMethodTypes;
+    pint_t demangledName;
 
 public:
+    pint_t getIsaVMAddr() const { return A::P::getP(isa); }
+    pint_t setIsaVMAddr(pint_t newIsa) { A::P::setP(isa, newIsa); }
+
+    const char *getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
+
+    uint32_t getSize() const { return A::P::E::get32(size); }
+    void setSize(uint32_t newSize) { A::P::E::set32(size, newSize); }
+
+    uint32_t getFlags() const { return A::P::E::get32(flags); }
+
+    void setFixedUp() { A::P::E::set32(flags, getFlags() | (1<<30)); }
+
+    objc_protocol_list_t<A> *getProtocols(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); }
+
     objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
 
     objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
@@ -446,6 +458,42 @@ public:
 
     objc_method_list_t<A> *getOptionalClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalClassMethods)); }
 
+    objc_property_list_t<A> *getInstanceProperties(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); }
+
+    pint_t *getExtendedMethodTypes(SharedCache<A>* cache) const {
+        if (getSize() < offsetof(objc_protocol_t<A>, extendedMethodTypes) + sizeof(extendedMethodTypes)) {
+            return NULL;
+        }
+        return (pint_t *)cache->mappedAddressForVMAddress(A::P::getP(extendedMethodTypes));
+    }
+
+    const char *getDemangledName(SharedCache<A>* cache) const {
+        if (sizeof(*this) < offsetof(objc_protocol_t<A>, demangledName) + sizeof(demangledName)) {
+            return NULL;
+        }
+        return (const char *)cache->mappedAddressForVMAddress(A::P::getP(demangledName));
+    }
+
+    void setDemangledName(SharedCache<A>* cache, const char *newName) {
+        if (sizeof(*this) < offsetof(objc_protocol_t<A>, demangledName) + sizeof(demangledName)) {
+            throw "objc protocol has the wrong size";
+        }
+        A::P::setP(demangledName, cache->VMAddressForMappedAddress(newName));
+    }
+
+    void addPointers(std::vector<void*>& pointersToAdd) 
+    {
+        pointersToAdd.push_back(&isa);
+        pointersToAdd.push_back(&name);
+        if (protocols) pointersToAdd.push_back(&protocols);
+        if (instanceMethods) pointersToAdd.push_back(&instanceMethods);
+        if (classMethods) pointersToAdd.push_back(&classMethods);
+        if (optionalInstanceMethods) pointersToAdd.push_back(&optionalInstanceMethods);
+        if (optionalClassMethods) pointersToAdd.push_back(&optionalClassMethods);
+        if (instanceProperties) pointersToAdd.push_back(&instanceProperties);
+        if (extendedMethodTypes) pointersToAdd.push_back(&extendedMethodTypes);
+        if (demangledName) pointersToAdd.push_back(&demangledName);
+    }
 };
 
 template <typename A>
@@ -460,14 +508,20 @@ public:
 
     pint_t getCount() const { return A::P::getP(count); }
 
+    pint_t getVMAddress(pint_t i) {
+        return A::P::getP(list[i]);
+    }
+
     objc_protocol_t<A>* get(SharedCache<A>* cache, pint_t i) {
-        return (objc_protocol_t<A>*)cache->mappedAddressForVMAddress(A::P::getP(list[i]));
+        return (objc_protocol_t<A>*)cache->mappedAddressForVMAddress(getVMAddress(i));
+    }
+
+    void setVMAddress(pint_t i, pint_t protoVMAddr) {
+        A::P::setP(list[i], protoVMAddr);
     }
     
-    void overwrite(pint_t& index, const objc_protocol_list_t<A>* src) {
-        pint_t srcCount = src->getCount();
-        memcpy(list+index, src->list, srcCount * sizeof(pint_t));
-        index += srcCount;
+    void set(SharedCache<A>* cache, pint_t i, objc_protocol_t<A>* proto) {
+        setVMAddress(i, cache->VMAddressForMappedAddress(proto));
     }
 
     uint32_t byteSize() const { 
@@ -719,13 +773,97 @@ public:
         for (pint_t i = 0; i < classes.count(); i++) {
             objc_class_t<A> *cls = classes.get(i);
             //fprintf(stderr, "visiting class: %s\n", cls->getName(cache));
-            classVisitor.visitClass(cache, header, cls);
+            if (cls) classVisitor.visitClass(cache, header, cls);
+        }
+    }
+};
+
+
+// Call visitor.visitProtocol() on every protocol.
+template <typename A, typename V>
+class ProtocolWalker {
+    typedef typename A::P P;
+    typedef typename A::P::uint_t pint_t;
+    V& protocolVisitor;
+public:
+    
+    ProtocolWalker(V& visitor) : protocolVisitor(visitor) { }
+    
+    void walk(SharedCache<A>* cache, const macho_header<P>* header)
+    {   
+        PointerSection<A, objc_protocol_t<A> *> 
+            protocols(cache, header, "__DATA", "__objc_protolist");
+        
+        for (pint_t i = 0; i < protocols.count(); i++) {
+            objc_protocol_t<A> *proto = protocols.get(i);
+            protocolVisitor.visitProtocol(cache, header, proto);
+        }
+    }
+};
+
+
+// Call visitor.visitProtocolReference() on every protocol.
+template <typename A, typename V>
+class ProtocolReferenceWalker {
+    typedef typename A::P P;
+    typedef typename A::P::uint_t pint_t;
+    V& mVisitor;
+
+    void visitProtocolList(SharedCache<A>* cache, 
+                           objc_protocol_list_t<A>* protolist)
+    {
+        if (!protolist) return;
+        for (pint_t i = 0; i < protolist->getCount(); i++) {
+            pint_t oldValue = protolist->getVMAddress(i);
+            pint_t newValue = mVisitor.visitProtocolReference(cache, oldValue);
+            protolist->setVMAddress(i, newValue);
+        }
+    }
+
+    friend class ClassWalker<A, ProtocolReferenceWalker<A, V>>;
+    void visitClass(SharedCache<A>* cache, const macho_header<P>*,
+                    objc_class_t<A>* cls) 
+    {
+        visitProtocolList(cache, cls->getProtocolList(cache));
+        visitProtocolList(cache, cls->getIsa(cache)->getProtocolList(cache));
+    }
+
+public:
+    
+    ProtocolReferenceWalker(V& visitor) : mVisitor(visitor) { }
+    void walk(SharedCache<A>* cache, const macho_header<P>* header)
+    {
+        // @protocol expressions
+        PointerSection<A, objc_protocol_t<A> *> 
+            protorefs(cache, header, "__DATA", "__objc_protorefs");
+        for (pint_t i = 0; i < protorefs.count(); i++) {
+            pint_t oldValue = protorefs.getVMAddress(i);
+            pint_t newValue = mVisitor.visitProtocolReference(cache, oldValue);
+            protorefs.setVMAddress(i, newValue);
+        }
+
+        // protocol lists in classes
+        ClassWalker<A, ProtocolReferenceWalker<A, V>> classes(*this);
+        classes.walk(cache, header);
+
+        // protocol lists in protocols
+        // __objc_protolists itself is NOT updated
+        PointerSection<A, objc_protocol_t<A> *> 
+            protocols(cache, header, "__DATA", "__objc_protolist");
+        for (pint_t i = 0; i < protocols.count(); i++) {
+            objc_protocol_t<A>* proto = protocols.get(i);
+            visitProtocolList(cache, proto->getProtocols(cache));
+            // not recursive: every old protocol object 
+            // must be in some protolist section somewhere
         }
     }
 };
 
 
-// Call visitor.visitMethodList(mlist) on every method list in a header.
+// Call visitor.visitMethodList(mlist) on every
+// class and category method list in a header.
+// Call visitor.visitProtocolMethodList(mlist, typelist) on every
+// protocol method list in a header.
 template <typename A, typename V>
 class MethodListWalker {
 
@@ -738,7 +876,7 @@ public:
     
     MethodListWalker(V& visitor) : mVisitor(visitor) { }
 
-    void walk(SharedCache<A>* cache, const macho_header<P>* header, bool walkProtocols)
+    void walk(SharedCache<A>* cache, const macho_header<P>* header)
     {   
         // Method lists in classes
         PointerSection<A, objc_class_t<A> *> 
@@ -769,27 +907,31 @@ public:
             }
         }
 
-               // Method description lists from protocols
-               if ( walkProtocols ) {
-                       PointerSection<A, objc_protocol_t<A> *> 
-                               protocols(cache, header, "__DATA", "__objc_protolist");
-                       for (pint_t i = 0; i < protocols.count(); i++) {
-                               objc_protocol_t<A> *proto = protocols.get(i);
-                               objc_method_list_t<A> *mlist;
-                               if ((mlist = proto->getInstanceMethods(cache))) {
-                                       mVisitor.visitMethodList(mlist);
-                               }
-                               if ((mlist = proto->getClassMethods(cache))) {
-                                       mVisitor.visitMethodList(mlist);
-                               }
-                               if ((mlist = proto->getOptionalInstanceMethods(cache))) {
-                                       mVisitor.visitMethodList(mlist);
-                               }
-                               if ((mlist = proto->getOptionalClassMethods(cache))) {
-                                       mVisitor.visitMethodList(mlist);
-                               }
-                       }
-               }
+        // Method description lists from protocols
+        PointerSection<A, objc_protocol_t<A> *>
+            protocols(cache, header, "__DATA", "__objc_protolist");
+        for (pint_t i = 0; i < protocols.count(); i++) {
+            objc_protocol_t<A> *proto = protocols.get(i);
+            objc_method_list_t<A> *mlist;
+            pint_t *typelist = proto->getExtendedMethodTypes(cache);
+
+            if ((mlist = proto->getInstanceMethods(cache))) {
+                mVisitor.visitProtocolMethodList(mlist, typelist);
+                if (typelist) typelist += mlist->getCount();
+            }
+            if ((mlist = proto->getClassMethods(cache))) {
+                mVisitor.visitProtocolMethodList(mlist, typelist);
+                if (typelist) typelist += mlist->getCount();
+            }
+            if ((mlist = proto->getOptionalInstanceMethods(cache))) {
+                mVisitor.visitProtocolMethodList(mlist, typelist);
+                if (typelist) typelist += mlist->getCount();
+            }
+            if ((mlist = proto->getOptionalClassMethods(cache))) {
+                mVisitor.visitProtocolMethodList(mlist, typelist);
+                if (typelist) typelist += mlist->getCount();
+            }
+        }
     }
 };
 
@@ -815,23 +957,28 @@ class SelectorOptimizer {
         // Do not setFixedUp: the methods are not yet sorted.
     }
 
+    void visitProtocolMethodList(objc_method_list_t<A> *mlist, pint_t *types)
+    {
+        visitMethodList(mlist);
+    }
+
 public:
 
     SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
 
     void optimize(SharedCache<A>* cache, const macho_header<P>* header)
     {
-        // method lists of all kinds
+        // method lists in classes, categories, and protocols
         MethodListWalker< A, SelectorOptimizer<A,V> > mw(*this);
-        mw.walk(cache, header, true);
+        mw.walk(cache, header);
         
         // @selector references
         PointerSection<A, const char *> 
             selrefs(cache, header, "__DATA", "__objc_selrefs");
         for (pint_t i = 0; i < selrefs.count(); i++) {
-            pint_t oldValue = selrefs.getUnmapped(i);
+            pint_t oldValue = selrefs.getVMAddress(i);
             pint_t newValue = mVisitor.visit(oldValue);
-            selrefs.set(i, newValue);
+            selrefs.setVMAddress(i, newValue);
         }
 
         // message references
@@ -847,6 +994,41 @@ public:
 };
 
 
+template <typename A>
+static bool headerSupportsGC(SharedCache<A>* cache, 
+                             const macho_header<typename A::P>* header)
+{
+    const macho_section<typename A::P> *imageInfoSection = 
+        header->getSection("__DATA", "__objc_imageinfo");
+    if (imageInfoSection) {
+        objc_image_info<A> *info = (objc_image_info<A> *)
+            cache->mappedAddressForVMAddress(imageInfoSection->addr());
+        return (info->supportsGCFlagSet()  ||  info->requiresGCFlagSet());
+    }
+
+    return false;
+}
+
+
+// Gather the set of GC-supporting classes
+template <typename A>
+class GCClassSet {
+    typedef typename A::P P;
+
+    std::set<objc_class_t<A>*> fGCClasses;
+    
+public:
+    bool contains(objc_class_t<A>* cls) const {
+        return fGCClasses.count(cls) != 0;
+    }
+
+    void visitClass(SharedCache<A>* cache, const macho_header<P>* header, objc_class_t<A> *cls) 
+    {
+        fGCClasses.insert(cls);
+    }
+};
+
+
 // Update selector references. The visitor performs recording and uniquing.
 template <typename A>
 class IvarOffsetOptimizer {
@@ -857,6 +1039,8 @@ class IvarOffsetOptimizer {
 
     uint32_t fOptimized;
 
+    GCClassSet<A> fGCClasses;
+
 public:
     
     IvarOffsetOptimizer() : fOptimized(0) { }
@@ -887,6 +1071,12 @@ public:
     // The slide algorithm is also implemented in objc. Any changes here should be reflected there also.
     void visitClass(SharedCache<A>* cache, const macho_header<P>* /*unused, may be NULL*/, objc_class_t<A> *cls)
     {
+        if (fGCClasses.contains(cls)) {
+            // This class supports GC. We don't know how to update 
+            // GC ivar layout bitmaps, so don't touch anything.
+            return;
+        }
+
         objc_class_t<A> *super = cls->getSuperclass(cache);
         if (super) {
             // Recursively visit superclasses to ensure we have the correct superclass start
@@ -919,20 +1109,24 @@ public:
             }
         }
     }
-    
+
+    // Gather the list of GC-supporting classes.
+    // Ivars in these classes cannot be updated because 
+    // we don't know how to update ivar layout bitmaps.
+    void findGCClasses(SharedCache<A>* cache, const macho_header<P>* header)
+    {
+        if (headerSupportsGC(cache, header)) {
+            ClassWalker<A, GCClassSet<A> > classVisitor(fGCClasses);
+            classVisitor.walk(cache, header);
+        }
+    }
+
     // Enumerates objc classes in the module and performs any ivar slides
     void optimize(SharedCache<A>* cache, const macho_header<P>* header)
     {
-        // The slide code cannot fix up GC layout strings so skip modules that support ore require GC
-        const macho_section<P> *imageInfoSection = header->getSection("__DATA", "__objc_imageinfo");
-        if (imageInfoSection) {
-            objc_image_info<A> *info = (objc_image_info<A> *)cache->mappedAddressForVMAddress(imageInfoSection->addr());
-            if (!info->supportsGCFlagSet() && !info->requiresGCFlagSet()) {
-                ClassWalker<A, IvarOffsetOptimizer<A> > classVisitor(*this);
-                classVisitor.walk(cache, header);
-            } else {
-                //fprintf(stderr, "GC support present - skipped module\n");
-            }
+        if (! headerSupportsGC(cache, header)) {
+            ClassWalker<A, IvarOffsetOptimizer<A> > classVisitor(*this);
+            classVisitor.walk(cache, header);
         }
     }
 };
@@ -956,6 +1150,25 @@ class MethodListSorter {
         fOptimized++;
     }
 
+    void visitProtocolMethodList(objc_method_list_t<A> *mlist, pint_t *typelist)
+    {
+        typename objc_method_t<A>::SortBySELAddress sorter;
+        // can't easily use std::stable_sort here
+        for (uint32_t i = 0; i < mlist->getCount(); i++) {
+            for (uint32_t j = i+1; j < mlist->getCount(); j++) {
+                objc_method_t<A>& mi = mlist->get(i);
+                objc_method_t<A>& mj = mlist->get(j);
+                if (! sorter(mi, mj)) {
+                    std::swap(mi, mj);
+                    if (typelist) std::swap(typelist[i], typelist[j]);
+                }
+            }
+        }
+
+        mlist->setFixedUp();
+        fOptimized++;
+    }
+
 public:
     MethodListSorter() : fOptimized(0) { }
 
@@ -964,7 +1177,7 @@ public:
     void optimize(SharedCache<A>* cache, macho_header<P>* header)
     {
         MethodListWalker<A, MethodListSorter<A> > mw(*this);
-        mw.walk(cache, header, false /* don't sort protocol method lists*/);
+        mw.walk(cache, header);
     }
 };
 
@@ -985,7 +1198,9 @@ public:
     {
         if (count == 0) return NULL;
 
-        if (bufSize < 2*sizeof(uint32_t) + count*sizeof(objc_header_info_t<A>)) {
+        size_t requiredSize = 
+            2*sizeof(uint32_t) + count*sizeof(objc_header_info_t<A>);
+        if (bufSize < requiredSize) {
             return "libobjc's read/write section is too small (metadata not optimized)";
         }
 
@@ -994,9 +1209,8 @@ public:
         A::P::E::set32(buf32[1], sizeof(objc_header_info_t<A>));
         fHinfos = (objc_header_info_t<A>*)(buf32+2);
 
-        size_t total = sizeof(uint32_t) + count*sizeof(objc_header_info_t<A>);
-        buf += total;
-        bufSize -= total;
+        buf += requiredSize;
+        bufSize -= requiredSize;
 
         return NULL;
     }
index b1fb172a4478cca18e8e4864cbc865fa0f92e030..e21a0f64faa89f61c1ce52888ea04798d820d0c8 100644 (file)
@@ -83,6 +83,16 @@ class NotReExportSymbol {
 public:
        NotReExportSymbol(const std::set<int> &rd) :_reexportDeps(rd) {}
        bool operator()(const mach_o::trie::Entry &entry) const {
+               bool result = isSymbolReExport(entry);
+               if (result) {
+                       // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
+                       ::free((void*)entry.name);
+                       const_cast<mach_o::trie::Entry*>(&entry)->name = NULL;
+               }
+               return result;
+       }
+private:
+       bool isSymbolReExport(const mach_o::trie::Entry &entry) const {
                if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR )
                        return true;
                if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 )
@@ -92,7 +102,6 @@ public:
                        return true;
                return false;
        }
-private:
        const std::set<int> &_reexportDeps;
 };
 
@@ -357,6 +366,12 @@ int optimize_linkedit(macho_header<typename A::P>* mh, uint64_t textOffsetInCach
        // return new size
        *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096);
        
+       // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
+       for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
+               ::free((void*)(it->name));
+       }
+       
+       
        return 0;
 }
 
index 477894b8e12d78f6bc338f04fc8e32ba649dd8fd..2c7c21203e1b9661300573a5500d1068ad83566b 100644 (file)
@@ -108,6 +108,8 @@ namespace dyld {
                                segInfo.name = segCmd->segname();
                                segInfo.fileOffset = fileOffset;
                                segInfo.fileSize = sizem;
+                               if ( segCmd->filesize() > segCmd->vmsize() )
+                                       return -1;
                                segInfo.address = segCmd->vmaddr();
                                callback(&dylibInfo, &segInfo);
                        }
index 9407ff62e7d753a728662980ba729084a6c7be1d..68625812fd04fc142f1abac6e4010392e33edc80 100644 (file)
@@ -44,6 +44,7 @@ struct dyld_cache_header
        uint64_t        localSymbolsOffset;             // file offset of where local symbols are stored
        uint64_t        localSymbolsSize;               // size of local symbols information
        uint8_t         uuid[16];                               // unique value for each shared cache file
+       uint64_t        cacheType;                              // 1 for development, 0 for optimized
 };
 
 struct dyld_cache_mapping_info {
index cc6dc0c46d2cbb910a998d4d8620817cc28b21ed..4ab3694ec27c57aa0b9b3b4cda7a23bc498ce109 100644 (file)
@@ -68,7 +68,7 @@
 #define SELOPT_WRITE
 #include "objc-shared-cache.h"
 
-#define FIRST_DYLIB_TEXT_OFFSET 0x8000
+#define FIRST_DYLIB_TEXT_OFFSET 0x10000
 
 #ifndef LC_FUNCTION_STARTS
     #define LC_FUNCTION_STARTS 0x26
@@ -77,6 +77,7 @@
 static bool                                                    verbose = false;
 static bool                                                    progress = false;
 static bool                                                    iPhoneOS = false;
+static bool                                                    rootless = true;
 static std::vector<const char*>                warnings;
 
 
@@ -125,6 +126,7 @@ public:
 
        static void                     addArchPair(ArchPair ap);
        static void                     addRoot(const char* vpath, const std::set<ArchPair>& archs);
+       static uint64_t         maxCacheSizeForArchPair(ArchPair ap);
        static void                     findSharedDylibs(ArchPair ap);
        static ArchGraph*       graphForArchPair(ArchPair ap) { return fgPerArchGraph[ap]; }
        static void                     setFileSystemRoot(const char* root) { fgFileSystemRoot = root; }
@@ -148,6 +150,8 @@ private:
                const MachOLayoutAbstraction*   getLayout() const { return fLayout; }
                size_t                                                  useCount() const { return fRootsDependentOnThis.size(); }
                bool                                                    allDependentsFound() const { return !fDependentMissing; }
+               bool                                                    dependsOnDylibList() const { return fRootsDependentOnThis.count(const_cast<DependencyNode*>(this)); }
+
        private:
                ArchGraph*                                                                      fGraph;
                const char*                                                                     fPath;
@@ -486,16 +490,32 @@ void ArchGraph::DependencyNode::markNeededByRoot(ArchGraph::DependencyNode* root
 }
 
 
+
 ArchGraph::DependencyNode::DependencyNode(ArchGraph* graph, const char* path, const MachOLayoutAbstraction* layout) 
  : fGraph(graph), fPath(strdup(path)), fLayout(layout), fDependenciesLoaded(false), fDependentMissing(false)
 {
        //fprintf(stderr, "new DependencyNode(0x%08X, %s)\n", graph->fArch, path);
 }
 
+uint64_t ArchGraph::maxCacheSizeForArchPair(ArchPair ap) {
+       switch ( ap.arch ) {
+               case CPU_TYPE_I386:
+                       return 0x20000000;
+               case CPU_TYPE_X86_64:
+                       return 0x40000000;
+               case CPU_TYPE_ARM:
+                       return ARM_SHARED_REGION_SIZE;
+               case CPU_TYPE_ARM64:
+                       return ARM64_SHARED_REGION_SIZE;
+               default: return UINT64_MAX;
+       }
+}
+
 void ArchGraph::findSharedDylibs(ArchPair ap)
 {
        const PathToNode& nodes = fgPerArchGraph[ap]->fNodes;
        std::set<const MachOLayoutAbstraction*> possibleLibs;
+       std::map<const MachOLayoutAbstraction*, const DependencyNode *> layoutToNode;
        //fprintf(stderr, "shared for arch %s\n", archName(ap));
        for(PathToNode::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
                DependencyNode* node = it->second;
@@ -506,9 +526,10 @@ void ArchGraph::findSharedDylibs(ArchPair ap)
                                char* msg;
                                if ( sharable(layout, ap, &msg) ) {
                                        possibleLibs.insert(layout);
+                                       layoutToNode[layout] = node;
                                }
                                else {
-                                       if ( layout->getID().name[0] == '@' ) {
+                                       if ( !iPhoneOS && (layout->getID().name[0] == '@') ) {
                                                // <rdar://problem/7770139> update_dyld_shared_cache should suppress warnings for embedded frameworks
                                        }
                                        else {
@@ -523,10 +544,49 @@ void ArchGraph::findSharedDylibs(ArchPair ap)
        // prune so that all shareable libs depend only on other shareable libs
        std::set<const MachOLayoutAbstraction*>& sharedLibs = fgPerArchGraph[ap]->fSharedDylibs;
        std::map<const MachOLayoutAbstraction*,bool> shareableMap;
+       uint64_t totalLibSize = 0;
        for (std::set<const MachOLayoutAbstraction*>::iterator lit = possibleLibs.begin(); lit != possibleLibs.end(); ++lit) {
-               if ( canBeShared(*lit, ap, possibleLibs, shareableMap) )
+               if ( canBeShared(*lit, ap, possibleLibs, shareableMap) ) {
+                       totalLibSize += (*lit)->getVMSize();
                        sharedLibs.insert(*lit);
+               }
        }
+
+#if 0  // disable auto-eviction because it happens before linkedit optimization which means it is overly conservative.
+
+       // Check to see if the unoptimized cache size is too large, if so trim out some libraries
+       uint64_t maxCacheSize = maxCacheSizeForArchPair(ap);
+       if (totalLibSize > maxCacheSize) {
+               fprintf(stderr, "update_dyld_shared_cache: unoptimized %s shared cache overflow, total VM space: %lldMB (max=%lldMB)\n", archName(ap), totalLibSize/(1024*1024), maxCacheSize/(1024*1024));
+               std::vector<const MachOLayoutAbstraction*> removableLibs;
+
+               for (const MachOLayoutAbstraction* layout : sharedLibs) {
+                       // Every library uses itself, and every MH_DYLIB has an extra useCount, so we know useCount of 2 implies nothing else in the shared cache uses it
+                       if (layoutToNode[layout]->useCount() == 2) {
+                               if ( layoutToNode[layout]->dependsOnDylibList() ) {
+                                       removableLibs.push_back(layout);
+                                       //fprintf(stderr, "  possible to evict: %s\n", layout->getID().name);
+                               }
+                       }
+               }
+
+               std::sort(removableLibs.begin(), removableLibs.end(),  [](const MachOLayoutAbstraction* a, const MachOLayoutAbstraction* b){
+                       return a->getVMSize() < b->getVMSize();
+               });
+
+               while ( (totalLibSize > maxCacheSize) && !removableLibs.empty() ) {
+                       const MachOLayoutAbstraction* largestRemovableLib = removableLibs.back();
+                       removableLibs.pop_back();
+                       if ( largestRemovableLib->getVMSize() > 1024*1024 )
+                               fprintf(stderr, "update_dyld_shared_cache: evicting % 3lldMB leaf dylib %s\n", largestRemovableLib->getVMSize()/(1024*1024), largestRemovableLib->getID().name);
+                       else
+                               fprintf(stderr, "update_dyld_shared_cache: evicting % 3lldKB leaf dylib %s\n", largestRemovableLib->getVMSize()/1024, largestRemovableLib->getID().name);
+                       sharedLibs.erase(largestRemovableLib);
+                       totalLibSize -= largestRemovableLib->getVMSize();
+               }
+               fprintf(stderr, "update_dyld_shared_cache: unoptimized %s shared cache reduced to total VM space: %lldMB\n", archName(ap), totalLibSize/1024/1024);
+       }
+#endif
 }
 
 const char*    ArchGraph::archName(ArchPair ap)
@@ -571,20 +631,19 @@ const char*       ArchGraph::archName(ArchPair ap)
 
 bool ArchGraph::sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char** msg)
 {
+       int trustErr = layout->notTrusted();
        if ( ! layout->isTwoLevelNamespace() ) 
                asprintf(msg, "can't put %s in shared cache because it was built -flat_namespace", layout->getID().name);
        else if ( ! layout->inSharableLocation() )
                asprintf(msg, "can't put %s in shared cache because its -install_name is not in /usr/lib or /System/Library", layout->getID().name);
        else if ( ! layout->hasSplitSegInfo() ) 
                asprintf(msg, "can't put %s in shared cache because it was not built for %s or later", layout->getID().name, (iPhoneOS ? "iPhoneOS 3.1" : "MacOSX 10.5"));
-       else if ( ! layout->isRootOwned() )
-               asprintf(msg, "can't put %s in shared cache because it is not owned by root", layout->getID().name);
+       else if ( rootless == true && trustErr != 0 )
+               asprintf(msg, "can't put %s in shared cache because it is not trusted: %s", layout->getFilePath(), strerror(trustErr));
        else if ( layout->hasDynamicLookupLinkage() )
                asprintf(msg, "can't put %s in shared cache because it was built with '-undefined dynamic_lookup'", layout->getID().name);
        else if ( layout->hasMainExecutableLookupLinkage() )
                asprintf(msg, "can't put %s in shared cache because it was built with '-bundle_loader'", layout->getID().name);
-       else if ( layout->hasMultipleReadWriteSegments() )
-               asprintf(msg, "can't put %s in shared cache because it has multiple r/w segments", layout->getID().name);
        else
                return true;
        return false;
@@ -670,7 +729,7 @@ private:
 
 
 StringPool::StringPool() 
-       : fBufferUsed(0), fBufferAllocated(64*1024*1024)
+       : fBufferUsed(0), fBufferAllocated(128*1024*1024)
 {
        fBuffer = (char*)malloc(fBufferAllocated);
 }
@@ -733,6 +792,7 @@ public:
                                                                                        bool alphaSort, bool verify, bool optimize, uint64_t dyldBaseAddress);
        bool                                    update(bool force, bool optimize, bool deleteExistingFirst, int archIndex, 
                                                                                int archCount, bool keepSignatures, bool dontMapLocalSymbols);
+       void                                    writeCacheFile(const char *cacheFilePath, uint8_t *cacheFileBuffer, uint32_t cacheFileSize, bool deleteOldCache);
        static const char*              cacheFileSuffix(bool optimized, const char* archName);
 
     // vm address = address AS WRITTEN into the cache
@@ -869,7 +929,7 @@ class PointerSection
     SharedCache<A>* const                      fCache;
     const macho_section<P>* const      fSection;
     pint_t * const                                     fBase;
-    uint64_t                                           fCount;
+    pint_t                                                     fCount;
 
 public:
     PointerSection(SharedCache<A>* cache, const macho_header<P>* header, 
@@ -881,25 +941,25 @@ public:
     {
     }
 
-    uint64_t count() const { return fCount; }
+    pint_t count() const { return fCount; }
 
-    uint64_t getUnmapped(uint64_t index) const {
+    pint_t getVMAddress(pint_t index) const {
         if (index >= fCount) throwf("index out of range");
         return P::getP(fBase[index]);
     }
 
-    T get(uint64_t index) const { 
-        return (T)fCache->mappedAddressForVMAddress(getUnmapped(index));
+    T get(pint_t index) const { 
+        return (T)fCache->mappedAddressForVMAddress(getVMAddress(index));
     }
 
-    void set(uint64_t index, uint64_t value) {
+    void setVMAddress(pint_t index, pint_t value) {
         if (index >= fCount) throwf("index out of range");
         P::setP(fBase[index], value);
     }
        
     void removeNulls() {
-        uint64_t shift = 0;
-        for (uint64_t i = 0; i < fCount; i++) {
+        pint_t shift = 0;
+        for (pint_t i = 0; i < fCount; i++) {
             pint_t value = fBase[i];
             if (value) {
                 fBase[i-shift] = value;
@@ -1067,6 +1127,11 @@ SharedCache<A>::SharedCache(ArchGraph* graph, const char* rootPath, const std::v
        else {
                fCacheFilePath = strdup(cachePathCanonical);
        }
+
+       // If the path we are writing to is trusted then our sources need to be trusted
+       // <rdar://problem/21166835> Can't update the update_dyld_shared_cache on a non-boot volume
+       rootless = rootless_check_trusted(fCacheFilePath);
+
        if ( overlayPaths.size() == 1 ) {
                // in overlay mode if there already is a cache file in the overlay,
                // check if it is up to date.  
@@ -1201,24 +1266,82 @@ void SharedCache<A>::assignNewBaseAddresses(bool verify)
        // align __TEXT region
        currentExecuteAddress = regionAlign(currentExecuteAddress);
 
-       // layout DATA for dylibs
+#define DENSE_PACK 0
+       // layout __DATA* segments
+       std::vector<MachOLayoutAbstraction::Segment*> dataSegs;
+       std::vector<MachOLayoutAbstraction::Segment*> dataConstSegs;
+       std::vector<MachOLayoutAbstraction::Segment*> dataDirtySegs;
        const uint64_t startWritableAddress = sharedRegionStartWritableAddress(currentExecuteAddress);
        uint64_t currentWritableAddress = startWritableAddress;
-       for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
-               std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
-               for (int i=0; i < segs.size(); ++i) {
-                       MachOLayoutAbstraction::Segment& seg = segs[i];
-                       seg.reset();
+       for (const LayoutInfo& info : fDylibs ) {
+               for (MachOLayoutAbstraction::Segment& seg : ((MachOLayoutAbstraction*)(info.layout))->getSegments()) {
                        if ( seg.writable() ) {
                                if ( seg.executable() ) 
                                        throw "found writable and executable segment";
-                               // __DATA segment
-                               seg.setNewAddress(currentWritableAddress);
-                               // <rdar://problem/13089366> always 4KB align data pages to allow padding to be removed
-                               currentWritableAddress = pageAlign4KB(seg.newAddress() + seg.size());
+                               seg.reset();
+                               if ( strcmp(seg.name(), "__DATA_CONST") == 0 )
+                                       dataConstSegs.push_back(&seg);
+                               else if ( strcmp(seg.name(), "__DATA_DIRTY") == 0 )
+                                       dataDirtySegs.push_back(&seg);
+                               else
+                                       dataSegs.push_back(&seg);
                        }
                }
        }
+       // coalesce all __DATA_CONST segments
+       for (MachOLayoutAbstraction::Segment* seg : dataConstSegs) {
+       #if DENSE_PACK
+               // start segment at needed alignment
+               currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment());
+               seg->setNewAddress(currentWritableAddress);
+               // pack together
+               uint64_t justSectionsSize = seg->sectionsSize();
+               currentWritableAddress = seg->newAddress() + justSectionsSize;
+               seg->setSize(justSectionsSize);
+               if ( seg->fileSize() > justSectionsSize )
+                       seg->setFileSize(justSectionsSize);
+       #else
+               seg->setNewAddress(currentWritableAddress);
+               // pack to 4KB pages
+               currentWritableAddress = pageAlign4KB(seg->newAddress() + seg->size());
+       #endif
+       }
+       #if DENSE_PACK
+       currentWritableAddress = pageAlign4KB(currentWritableAddress);
+       #endif
+       // coalesce all __DATA segments
+       for (MachOLayoutAbstraction::Segment* seg : dataSegs) {
+       #if DENSE_PACK
+               // start segment at needed alignment
+               currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment());
+               seg->setNewAddress(currentWritableAddress);
+               // pack together
+               uint64_t justSectionsSize = seg->sectionsSize();
+               currentWritableAddress = seg->newAddress() + justSectionsSize;
+               seg->setSize(justSectionsSize);
+               if ( seg->fileSize() > justSectionsSize )
+                       seg->setFileSize(justSectionsSize);
+       #else
+               seg->setNewAddress(currentWritableAddress);
+               // pack to 4KB pages
+               currentWritableAddress = pageAlign4KB(seg->newAddress() + seg->size());
+       #endif
+       }
+       #if DENSE_PACK
+       currentWritableAddress = pageAlign4KB(currentWritableAddress);
+       #endif
+       // coalesce all __DATA_DIRTY segments
+       for (MachOLayoutAbstraction::Segment* seg : dataDirtySegs) {
+               // start segment at needed alignment
+               currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment());
+               seg->setNewAddress(currentWritableAddress);
+               // pack together
+               uint64_t justSectionsSize = seg->sectionsSize();
+               currentWritableAddress = seg->newAddress() + justSectionsSize;
+               seg->setSize(justSectionsSize);
+               if ( seg->fileSize() > justSectionsSize )
+                       seg->setFileSize(justSectionsSize);
+       }
        // align __DATA region
        currentWritableAddress = regionAlign(currentWritableAddress);
 
@@ -1869,6 +1992,8 @@ void LinkEditOptimizer<A>::updateLoadCommands(uint64_t newVMAddress, uint64_t le
                                for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
                                        if ( sect->offset() != 0 )
                                                sect->set_offset(sect->offset()+fileOffsetDelta);
+                                               //if ( (sect->flags() & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS )
+                                               //      fprintf(stderr, "found initializer(s) in %s\n", fLayout.getFilePath());
                                }
                        }
                }
@@ -1933,6 +2058,7 @@ void LinkEditOptimizer<A>::updateLoadCommands(uint64_t newVMAddress, uint64_t le
                switch ( srcCmd->cmd() ) {
                        case LC_SEGMENT_SPLIT_INFO:
                        case LC_DYLIB_CODE_SIGN_DRS:
+                       case LC_RPATH:
                                // don't copy
                                break;
                        case LC_CODE_SIGNATURE:
@@ -2175,6 +2301,140 @@ public:
 };
 
 
+template <typename A>
+class ProtocolOptimizer
+{
+private:
+    typedef typename A::P P;
+    typedef typename A::P::uint_t pint_t;
+
+    objc_opt::string_map fProtocolNames;
+    objc_opt::protocol_map fProtocols;
+    size_t fProtocolCount;
+    size_t fProtocolReferenceCount;
+
+    friend class ProtocolReferenceWalker<A, ProtocolOptimizer<A>>;
+    pint_t visitProtocolReference(SharedCache<A>* cache, pint_t oldValue)
+    {
+        objc_protocol_t<A>* proto = (objc_protocol_t<A>*)
+            cache->mappedAddressForVMAddress(oldValue);
+        pint_t newValue = fProtocols[proto->getName(cache)];
+        if (oldValue != newValue) fProtocolReferenceCount++;
+        return newValue;
+    }
+
+public:
+
+    ProtocolOptimizer()
+        : fProtocolNames()
+        , fProtocols()
+        , fProtocolCount(0)
+        , fProtocolReferenceCount(0)
+    { }
+
+    void addProtocols(SharedCache<A>* cache, 
+                      const macho_header<P>* header)
+    {
+        PointerSection<A, objc_protocol_t<A> *> 
+            protocols(cache, header, "__DATA", "__objc_protolist");
+        
+        for (pint_t i = 0; i < protocols.count(); i++) {
+            objc_protocol_t<A> *proto = protocols.get(i);
+
+            const char *name = proto->getName(cache);
+            if (fProtocolNames.count(name) == 0) {
+                // Need a Swift demangler API in OS before we can handle this
+                if (0 == strncmp(name, "_TtP", 4)) {
+                    throw "objc protocol has Swift name";
+                }
+                if (proto->getSize() > sizeof(objc_protocol_t<A>)) {
+                    throw "objc protocol is too big";
+                }
+
+                uint64_t name_vmaddr = cache->VMAddressForMappedAddress(name);
+                uint64_t proto_vmaddr = cache->VMAddressForMappedAddress(proto);
+                fProtocolNames.insert(objc_opt::string_map::value_type(name, name_vmaddr));
+                fProtocols.insert(objc_opt::protocol_map::value_type(name, proto_vmaddr));
+                fProtocolCount++;
+            }
+        }
+    }
+
+    const char *writeProtocols(SharedCache<A>* cache, 
+                               uint8_t *& dest, size_t& remaining, 
+                               std::vector<void*>& pointersInData, 
+                               pint_t protocolClassVMAddr)
+    {
+        if (fProtocolCount == 0) return NULL;
+
+        if (protocolClassVMAddr == 0) {
+            return "libobjc's Protocol class symbol not found (metadata not optimized)";
+        }
+
+        size_t required = fProtocolCount * sizeof(objc_protocol_t<A>);
+        if (remaining < required) {
+            return "libobjc's read-write section is too small (metadata not optimized)";
+        }
+
+        for (objc_opt::protocol_map::iterator iter = fProtocols.begin();
+             iter != fProtocols.end();
+             ++iter)
+        {
+            objc_protocol_t<A>* oldProto = (objc_protocol_t<A>*)
+                cache->mappedAddressForVMAddress(iter->second);
+
+            // Create a new protocol object.
+            objc_protocol_t<A>* proto = (objc_protocol_t<A>*)dest;
+            dest += sizeof(*proto);
+            remaining -= sizeof(*proto);
+
+            // Initialize it.
+            uint32_t oldSize = oldProto->getSize();
+            memcpy(proto, oldProto, oldSize);
+            if (!proto->getIsaVMAddr()) {
+                proto->setIsaVMAddr(protocolClassVMAddr);
+            }
+            if (oldSize < sizeof(*proto)) {
+                // Protocol object is old. Populate new fields.
+                proto->setSize(sizeof(objc_protocol_t<A>));
+                // missing extendedMethodTypes is already nil
+            }
+            // Some protocol objects are big enough to have the 
+            // demangledName field but don't initialize it.
+            if (! proto->getDemangledName(cache)) {
+                proto->setDemangledName(cache, proto->getName(cache));
+            }
+            proto->setFixedUp();
+
+            // Redirect the protocol table at our new object.
+            iter->second = cache->VMAddressForMappedAddress(proto);
+
+            // Add new rebase entries.
+            proto->addPointers(pointersInData);
+        }
+        
+        return NULL;
+    }
+
+    void updateReferences(SharedCache<A>* cache, const macho_header<P>* header)
+    {
+        ProtocolReferenceWalker<A, ProtocolOptimizer<A>> refs(*this);
+        refs.walk(cache, header);
+    }
+
+    objc_opt::string_map& protocolNames() { 
+        return fProtocolNames;
+    }
+
+    objc_opt::protocol_map& protocols() { 
+        return fProtocols;
+    }
+
+    size_t protocolCount() const { return fProtocolCount; }
+    size_t protocolReferenceCount() const { return fProtocolReferenceCount; }
+};
+
+
 static int percent(size_t num, size_t denom) {
     if (denom) return (int)(num / (double)denom * 100);
     else return 100;
@@ -2194,14 +2454,17 @@ void SharedCache<A>::optimizeObjC(std::vector<void*>& pointersInData)
                warn(archName(), "libobjc's optimization structure size is wrong (metadata not optimized)");
     }
 
-    // Find libobjc's empty sections to fill in
+    // Find libobjc's empty sections to fill in.
+    // Find libobjc's list of pointers for us to use.
     const macho_section<P> *optROSection = NULL;
     const macho_section<P> *optRWSection = NULL;
+    const macho_section<P> *optPointerListSection = NULL;
        for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
-        if ( strstr(it->layout->getFilePath(), "libobjc") != NULL ) {
+        if ( strstr(it->layout->getFilePath(), "/libobjc.") != NULL ) {
                        const macho_header<P>* mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
                        optROSection = mh->getSection("__TEXT", "__objc_opt_ro");
                        optRWSection = mh->getSection("__DATA", "__objc_opt_rw");
+                       optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs");
                        break;
                }
        }
@@ -2215,6 +2478,11 @@ void SharedCache<A>::optimizeObjC(std::vector<void*>& pointersInData)
                warn(archName(), "libobjc's read/write section missing (metadata not optimized)");
                return;
        }
+       
+       if ( optPointerListSection == NULL ) {
+               warn(archName(), "libobjc's pointer list section missing (metadata not optimized)");
+               return;
+       }
 
        uint8_t* optROData = (uint8_t*)mappedAddressForVMAddress(optROSection->addr());
     size_t optRORemaining = optROSection->size();
@@ -2235,6 +2503,12 @@ void SharedCache<A>::optimizeObjC(std::vector<void*>& pointersInData)
                return;
        }
 
+    if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
+        warn(archName(), "libobjc's pointer list section is too small (metadata not optimized)");
+               return;
+    }
+    const objc_opt::objc_opt_pointerlist_tt<pint_t> *optPointerList = (const objc_opt::objc_opt_pointerlist_tt<pint_t> *)mappedAddressForVMAddress(optPointerListSection->addr());
+
     // Write nothing to optROHeader until everything else is written.
     // If something fails below, libobjc will not use the section.
 
@@ -2334,11 +2608,57 @@ void SharedCache<A>::optimizeObjC(std::vector<void*>& pointersInData)
     }
 
 
+    // Unique protocols and build protocol table.
+
+    // This is SAFE: no protocol references are updated yet
+    // This must be done AFTER updating method lists.
+
+    ProtocolOptimizer<A> protocolOptimizer;
+       for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
+        const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
+        protocolOptimizer.addProtocols(this, mh);
+       }
+
+    pint_t protocolClassVMAddr = P::getP(optPointerList->protocolClass);
+    err = protocolOptimizer.writeProtocols(this, optRWData, optRWRemaining, 
+                                           pointersInData, protocolClassVMAddr);
+    if (err) {
+        warn(archName(), err);
+        return;
+    }
+
+    uint64_t protocoloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining;
+    objc_opt::objc_protocolopt_t *protocolopt = new(optROData) objc_opt::objc_protocolopt_t;
+    err = protocolopt->write(protocoloptVMAddr, optRORemaining, 
+                             protocolOptimizer.protocolNames(), 
+                             protocolOptimizer.protocols(), verbose);
+    if (err) {
+        warn(archName(), err);
+        return;
+    }
+    optROData += protocolopt->size();
+    optRORemaining -= protocolopt->size();
+    protocolopt->byteswap(E::little_endian), protocolopt = NULL;
+
+
+    // Redirect protocol references to the uniqued protocols.
+
+    // This is SAFE: the new protocol objects are still usable as-is.
+       for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
+        const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
+        protocolOptimizer.updateReferences(this, mh);
+       }
+
+
     // Repair ivar offsets.
 
     // This is SAFE: the runtime always validates ivar offsets at runtime.
 
     IvarOffsetOptimizer<A> ivarOffsetOptimizer;
+       for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
+        const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
+        ivarOffsetOptimizer.findGCClasses(this, mh);
+       }
        for(typename std::vector<LayoutInfo>::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) {
         const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
         ivarOffsetOptimizer.optimize(this, mh);
@@ -2364,6 +2684,7 @@ void SharedCache<A>::optimizeObjC(std::vector<void*>& pointersInData)
     // Success. Update RO header last.
     E::set32(optROHeader->selopt_offset, seloptVMAddr - optROSection->addr());
     E::set32(optROHeader->clsopt_offset, clsoptVMAddr - optROSection->addr());
+    E::set32(optROHeader->protocolopt_offset, protocoloptVMAddr - optROSection->addr());
     E::set32(optROHeader->headeropt_offset, hinfoVMAddr - optROSection->addr());
 
     if ( verbose ) {
@@ -2383,6 +2704,12 @@ void SharedCache<A>::optimizeObjC(std::vector<void*>& pointersInData)
         fprintf(stderr, "update_dyld_shared_cache: for %s, "
                 "updated %zu selector references\n", 
                 archName(), uniq.count());
+        fprintf(stderr, "update_dyld_shared_cache: for %s, "
+                "uniqued %zu protocols\n", 
+                archName(), protocolOptimizer.protocolCount());
+        fprintf(stderr, "update_dyld_shared_cache: for %s, "
+                "updated %zu protocol references\n", 
+                archName(), protocolOptimizer.protocolReferenceCount());
         fprintf(stderr, "update_dyld_shared_cache: for %s, "
                 "updated %zu ivar offsets\n", 
                 archName(), ivarOffsetOptimizer.optimized());
@@ -2476,12 +2803,108 @@ static bool adhoc_codesign_share_cache(const char* path)
        return true;
 }
 
+template <typename A>
+void SharedCache<A>::writeCacheFile(const char *cacheFilePath, uint8_t *cacheFileBuffer, uint32_t cacheFileSize, bool deleteOldCache) {
+       char tempCachePath[strlen(cacheFilePath)+16];
+       sprintf(tempCachePath, "%s.tmp%u", cacheFilePath, getpid());
+
+       try {
+               // install signal handlers to delete temp file if program is killed
+               sCleanupFile = tempCachePath;
+               ::signal(SIGINT, cleanup);
+               ::signal(SIGBUS, cleanup);
+               ::signal(SIGSEGV, cleanup);
+
+               // compute UUID of whole cache
+               uint8_t digest[16];
+               CC_MD5(cacheFileBuffer, cacheFileSize, digest);
+               // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
+               digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 );
+               digest[8] = ( digest[8] & 0x3F ) | 0x80;
+               ((dyldCacheHeader<E>*)cacheFileBuffer)->set_uuid(digest);
+
+               // create var/db/dyld dirs if needed
+               char dyldDirs[1024];
+               strcpy(dyldDirs, cacheFilePath);
+               char* lastSlash = strrchr(dyldDirs, '/');
+               if ( lastSlash != NULL )
+                       lastSlash[1] = '\0';
+               struct stat stat_buf;
+               if ( stat(dyldDirs, &stat_buf) != 0 ) {
+                       const char* afterSlash = &dyldDirs[1];
+                       char* slash;
+                       while ( (slash = strchr(afterSlash, '/')) != NULL ) {
+                               *slash = '\0';
+                               ::mkdir(dyldDirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+                               *slash = '/';
+                               afterSlash = slash+1;
+                       }
+               }
+
+               // create temp file for cache
+               int fd = ::open(tempCachePath, O_CREAT | O_RDWR | O_TRUNC, 0644);
+               if ( fd == -1 )
+                       throwf("can't create temp file %s, errno=%d", tempCachePath, errno);
+
+               // try to allocate whole cache file contiguously
+               fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, cacheFileSize, 0 };
+               ::fcntl(fd, F_PREALLOCATE, &fcntlSpec);
+
+               // write out cache file
+               if ( verbose )
+                       fprintf(stderr, "update_dyld_shared_cache: writing cache to disk: %s\n", tempCachePath);
+               if ( ::pwrite(fd, cacheFileBuffer, cacheFileSize, 0) != cacheFileSize )
+                       throwf("write() failure creating cache file, errno=%d", errno);
+
+               // flush to disk and close
+               int result = ::fcntl(fd, F_FULLFSYNC, NULL);
+               if ( result == -1 )
+                       fprintf(stderr, "update_dyld_shared_cache: warning, fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, tempCachePath);
+               result = ::close(fd);
+               if ( result != 0 )
+                       fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath);
+
+               if ( !iPhoneOS )
+                       adhoc_codesign_share_cache(tempCachePath);
+
+               if ( deleteOldCache ) {
+                       const char* pathLastSlash = strrchr(cacheFilePath, '/');
+                       if ( pathLastSlash != NULL ) {
+                               result = ::unlink(cacheFilePath);
+                               if ( result != 0 ) {
+                                       if ( errno != ENOENT )
+                                               fprintf(stderr, "update_dyld_shared_cache: warning, unable to remove existing cache %s because errno=%d\n", cacheFilePath, errno);
+                               }
+                       }
+               }
+
+               // move new cache file to correct location for use after reboot
+               if ( verbose )
+                       fprintf(stderr, "update_dyld_shared_cache: atomically moving cache file into place: %s\n", cacheFilePath);
+               result = ::rename(tempCachePath, cacheFilePath);
+               if ( result != 0 )
+                       throwf("can't swap newly create dyld shared cache file: rename(%s,%s) returned errno=%d", tempCachePath, cacheFilePath, errno);
+
+               // flush everything to disk to assure rename() gets recorded
+               sync_volume(cacheFilePath);
+
+               // restore default signal handlers
+               ::signal(SIGINT, SIG_DFL);
+               ::signal(SIGBUS, SIG_DFL);
+               ::signal(SIGSEGV, SIG_DFL);
+       }
+       catch (...){
+               // remove temp cache file
+               ::unlink(tempCachePath);
+               throw;
+       }
+}
 
 
 template <>     bool   SharedCache<x86_64>::addCacheSlideInfo(){ return true; }
 template <>     bool   SharedCache<arm>::addCacheSlideInfo()   { return true; }
 template <>     bool   SharedCache<x86>::addCacheSlideInfo()   { return false; }
-template <>     bool   SharedCache<arm64>::addCacheSlideInfo() { return true; } 
+template <>     bool   SharedCache<arm64>::addCacheSlideInfo() { return true; }
 
 
 template <typename A>
@@ -2489,6 +2912,12 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                                                int archCount, bool keepSignatures, bool dontMapLocalSymbols)
 {
        bool didUpdate = false;
+       bool canEmitDevelopmentCache = true;
+       char devCacheFilePath[strlen(fCacheFilePath)+strlen(".development")];
+       char fileListFilePath[strlen(fCacheFilePath)+strlen(".list")];
+       sprintf(devCacheFilePath, "%s.development", fCacheFilePath);
+       sprintf(fileListFilePath, "%s.list", fCacheFilePath);
+       std::vector<const char *> paths;
        
        // already up to date?
        if ( force || fExistingIsNotUpToDate ) {
@@ -2504,8 +2933,6 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                        ::unlink(fCacheFilePath);
                uint8_t* inMemoryCache = NULL;
                uint32_t allocatedCacheSize = 0;
-               char tempCachePath[strlen(fCacheFilePath)+16];
-               sprintf(tempCachePath, "%s.tmp%u", fCacheFilePath, getpid());
                try {
                        // allocate a memory block to hold cache
                        uint32_t cacheFileSize = 0;
@@ -2578,11 +3005,12 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                        const int dylibCount = fDylibs.size();
                        int dylibIndex = 0;
                        int progressIndex = 0;
+                       bool foundLibSystem = false;
                        for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++dylibIndex) {
                                const char* path = it->layout->getFilePath();
                                int src = ::open(path, O_RDONLY, 0);
                                if ( src == -1 )
-                                       throwf("can't open file %s, errnor=%d", it->layout->getID().name, errno);
+                                       throwf("can't open file %s, errno=%d", it->layout->getID().name, errno);
                                // mark source as "don't cache"
                                (void)fcntl(src, F_NOCACHE, 1);
                                // verify file has not changed since dependency analysis
@@ -2593,15 +3021,18 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                        throwf("file inode changed from %llu to %llu during cache creation: %s", it->layout->getInode(), stat_buf.st_ino, path);
                                else if ( it->layout->getLastModTime() != stat_buf.st_mtime )
                                        throwf("file mtime changed from 0x%lX to 0x%lX during cache creation: %s", it->layout->getLastModTime(), stat_buf.st_mtime, path);
-
+                               if ( strcmp(it->layout->getID().name, "/usr/lib/libSystem.B.dylib") == 0 )
+                                       foundLibSystem = true;
                                if ( verbose )
                                        fprintf(stderr, "update_dyld_shared_cache: copying %s to cache\n", it->layout->getFilePath());
                                try {
                                        const std::vector<MachOLayoutAbstraction::Segment>& segs = it->layout->getSegments();
                                        for (int i=0; i < segs.size(); ++i) {
                                                const MachOLayoutAbstraction::Segment& seg = segs[i];
-                                               if ( verbose )
-                                                       fprintf(stderr, "\t\tsegment %s, size=0x%0llX, cache address=0x%0llX\n", seg.name(), seg.fileSize(), seg.newAddress());
+                                               if ( verbose ) {
+                                                       fprintf(stderr, "\t\tsegment %s, size=0x%0llX, cache address=0x%0llX, buffer address=%p\n",
+                                                               seg.name(), seg.size(), seg.newAddress(), &inMemoryCache[cacheFileOffsetForVMAddress(seg.newAddress())]);
+                                               }
                                                if ( seg.size() > 0 ) {
                                                        const uint64_t segmentSrcStartOffset = it->layout->getOffsetInUniversalFile()+seg.fileOffset();
                                                        const uint64_t segmentSize = seg.fileSize();
@@ -2621,6 +3052,7 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                        throwf("%s while copying %s to shared cache", msg, it->layout->getID().name);
                                }
                                ::close(src);
+                               paths.push_back(it->layout->getID().name);
                                if ( progress ) {
                                        // assuming read takes 40% of time
                                        int nextProgressIndex = archIndex*100+(40*dylibIndex)/dylibCount;
@@ -2629,7 +3061,9 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                        progressIndex = nextProgressIndex;
                                }
                        }
-                                               
+                       if ( !foundLibSystem )
+                               throw "cache would be missing required dylib /usr/lib/libSystem.B.dylib";
+
                        // set mapped address for each segment
                        for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
                                std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
@@ -2649,7 +3083,10 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                        for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
                                try {
                                        Rebaser<A> r(*it->layout);
-                                       r.rebase(pointersInData);
+                                       if (!r.rebase(pointersInData)) {
+                                               canEmitDevelopmentCache = false;
+                                               fprintf(stderr, "update_dyld_shared_cache: Omitting development cache for %s, cannot rebase dylib into place for %s\n", archName(), it->layout->getID().name);
+                                       }
                                        //if ( verbose )
                                        //      fprintf(stderr, "update_dyld_shared_cache: for %s, rebasing dylib into cache for %s\n", archName(), it->layout->getID().name);
                                }
@@ -2665,13 +3102,12 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                        std::vector<Binder<A>*> binders;
                        for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
                                //fprintf(stderr, "binding %s\n", it->layout->getID().name);
-                               Binder<A>* binder = new Binder<A>(*it->layout, fDyldBaseAddress);
+                               Binder<A>* binder = new Binder<A>(*it->layout);
                                binders.push_back(binder);
                                // only add dylibs to map
                                if ( it->layout->getID().name != NULL )
                                        map[it->layout->getID().name] = binder;
                        }
-                       
                        // tell each Binder about the others
                        for(typename std::vector<Binder<A>*>::iterator it = binders.begin(); it != binders.end(); ++it) {
                                (*it)->setDependentBinders(map);
@@ -2687,6 +3123,46 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                        throwf("%s in %s", msg, (*it)->getDylibID());
                                }
                        }
+
+                       for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
+                               const macho_header<P>* fHeader = (const macho_header<P>*)it->layout->getSegments()[0].mappedAddress();
+                               const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+                               const uint32_t cmd_count = fHeader->ncmds();
+                               const macho_load_command<P>* cmd = cmds;
+                               macho_dyld_info_command<P>*                                     fDyldInfo;
+                               uint64_t originalLinkEditVMAddr = 0;
+                               for (uint32_t i = 0; i < cmd_count; ++i) {
+                                       if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+                                               macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+                                               if ( strcmp(seg->segname(), "__LINKEDIT") != 0 ) {
+                                                       pint_t oldFileOff = seg->fileoff();
+                                                       originalLinkEditVMAddr += seg->vmsize();
+                                                       // don't alter __TEXT until <rdar://problem/7022345> is fixed
+                                                       if ( strcmp(seg->segname(), "__TEXT") != 0 ) {
+                                                               // update all other segments fileoff to be offset from start of cache file
+                                                               seg->set_fileoff(cacheFileOffsetForVMAddress(seg->vmaddr()));
+                                                       }
+                                                       pint_t fileOffsetDelta = seg->fileoff() - oldFileOff;
+                                                       const MachOLayoutAbstraction::Segment* layoutSeg = it->layout->getSegment(seg->segname());
+                                                       if ( layoutSeg != NULL ) {
+                                                               seg->set_vmsize(layoutSeg->size());
+                                                               seg->set_filesize(layoutSeg->fileSize());
+                                                       }
+                                                       // update all sections in this segment
+                                                       macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+                                                       macho_section<P>* const sectionsEnd = &sectionsStart[seg->nsects()];
+                                                       for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+                                                               if ( sect->offset() != 0 )
+                                                                       sect->set_offset(sect->offset()+fileOffsetDelta);
+                                                       }
+                                               }
+                                       } else if (cmd->cmd() == LC_DYLD_INFO || cmd->cmd() == LC_DYLD_INFO_ONLY) {
+                                               fDyldInfo = (macho_dyld_info_command<P>*)cmd;
+                                       }
+                                       cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+                               }
+                       }
+
                        // optimize binding
                        for(typename std::vector<Binder<A>*>::iterator it = binders.begin(); it != binders.end(); ++it) {
                                try {
@@ -2696,6 +3172,7 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                        throwf("%s in %s", msg, (*it)->getDylibID());
                                }
                        }
+
                        // delete binders
                        for(typename std::vector<Binder<A>*>::iterator it = binders.begin(); it != binders.end(); ++it) {
                                delete *it;
@@ -2703,9 +3180,11 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
        
                        // merge/optimize all LINKEDIT segments
                        if ( optimize ) {
-                               //fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024));
+                               if ( verbose )
+                                       fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024));
                                cacheFileSize = (this->optimizeLINKEDIT(keepSignatures, dontMapLocalSymbols) - inMemoryCache);
-                               //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size 0x%08X %uMB\n", cacheFileSize, cacheFileSize/(1024*1024));
+                               if ( verbose )
+                                       fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024));
                                // update header to reduce mapping size
                                dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
                                dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&inMemoryCache[sizeof(dyldCacheHeader<E>)];
@@ -2718,7 +3197,22 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                //              header->codeSignatureOffset(), fMappings.back().sfm_address + fMappings.back().sfm_size);
                                header->set_codeSignatureOffset(fMappings.back().sfm_file_offset + fMappings.back().sfm_size);
                        }
-                       
+
+                       // dump dev cache with optimized linkedit, but not ObjC optimizations
+                       if (iPhoneOS && canEmitDevelopmentCache) {
+                               int fileListFD = ::open(fileListFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+                               if ( fileListFD != -1 ) {
+                                       for (const char* path : paths) {
+                                               write(fileListFD, path, strlen(path)+1);
+                                               write(fileListFD, "\n", 1);
+                                       }
+                                       close(fileListFD);
+                               }
+
+                               ((dyldCacheHeader<E>*)inMemoryCache)->set_cacheType(1);
+                               writeCacheFile(devCacheFilePath, inMemoryCache, cacheFileSize, fCacheFileInFinalLocation);
+                       }
+
                        // unique objc selectors and update other objc metadata
             if ( optimize ) {
                                optimizeObjC(pointersInData);
@@ -2859,20 +3353,12 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                for (int i=0; i < cacheHeader->mappingCount(); ++i) {
                                        uint64_t endAddr = mappings[i].address() + mappings[i].size() + estCodeSigSize;
                                        if ( endAddr > (sharedRegionStartAddress() + sharedRegionSize()) ) {
-                                               throwf("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regionsaddress space.  Overflow amount: %lluKB\n",
+                                               throwf("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regions address space.  Overflow amount: %lluKB\n",
                                                        getpid(), fArchGraph->archName(), (endAddr-(sharedRegionStartAddress() + sharedRegionSize()))/1024);
                                        }
                                }
                        }
                        
-                       // compute UUID of whole cache
-                       uint8_t digest[16];
-                       CC_MD5(inMemoryCache, cacheFileSize, digest);
-                       // <rdar://problem/6723729> uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats
-                       digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 );
-                       digest[8] = ( digest[8] & 0x3F ) | 0x80;
-                       ((dyldCacheHeader<E>*)inMemoryCache)->set_uuid(digest);
-                       
                        if ( fVerify ) {
                                // if no existing cache, say so
                                if ( fExistingCacheForVerification == NULL ) {
@@ -2935,98 +3421,9 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                }
                        }
                        else {
-                               // install signal handlers to delete temp file if program is killed 
-                               sCleanupFile = tempCachePath;
-                               ::signal(SIGINT, cleanup);
-                               ::signal(SIGBUS, cleanup);
-                               ::signal(SIGSEGV, cleanup);
-                               
-                               // create var/db/dyld dirs if needed
-                               char dyldDirs[1024];
-                               strcpy(dyldDirs, fCacheFilePath);
-                               char* lastSlash = strrchr(dyldDirs, '/');
-                               if ( lastSlash != NULL )
-                                       lastSlash[1] = '\0';
-                               struct stat stat_buf;
-                               if ( stat(dyldDirs, &stat_buf) != 0 ) {
-                                       const char* afterSlash = &dyldDirs[1];
-                                       char* slash;
-                                       while ( (slash = strchr(afterSlash, '/')) != NULL ) {
-                                               *slash = '\0';
-                                               ::mkdir(dyldDirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
-                                               *slash = '/';
-                                               afterSlash = slash+1;
-                                       }
-                               }
-                               
-                               // create temp file for cache
-                               int fd = ::open(tempCachePath, O_CREAT | O_RDWR | O_TRUNC, 0644);       
-                               if ( fd == -1 )
-                                       throwf("can't create temp file %s, errnor=%d", tempCachePath, errno);
-                                       
-                               // try to allocate whole cache file contiguously
-                               fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, cacheFileSize, 0 };
-                               ::fcntl(fd, F_PREALLOCATE, &fcntlSpec);
-
-                               // write out cache file
-                               if ( verbose )
-                                       fprintf(stderr, "update_dyld_shared_cache: writing cache to disk: %s\n", tempCachePath);
-                               if ( ::pwrite(fd, inMemoryCache, cacheFileSize, 0) != cacheFileSize )
-                                       throwf("write() failure creating cache file, errno=%d", errno);
-                               if ( progress ) {
-                                       // assuming write takes 35% of time
-                                       fprintf(stdout, "%3u/%u\n", (archIndex+1)*90, archCount*100);
-                               }
-                               
-                               // flush to disk and close
-                               int result = ::fcntl(fd, F_FULLFSYNC, NULL);
-                               if ( result == -1 ) 
-                                       fprintf(stderr, "update_dyld_shared_cache: warning, fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, tempCachePath);
-                               result = ::close(fd);
-                               if ( result != 0 ) 
-                                       fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath);
-                               
-                               if ( !iPhoneOS )
-                                       adhoc_codesign_share_cache(tempCachePath);
-
-                               // <rdar://problem/7901042> Make life easier for the kernel at shutdown.
-                               // If we just move the new cache file over the old, the old file
-                               // may need to exist in the open-unlink state.  But because it
-                               // may be mapped into the shared region, it cannot be deleted
-                               // until all user processes are terminated.  That leaves are
-                               // small to non-existent window for the kernel to delete the
-                               // old cache file.
-                               if ( fCacheFileInFinalLocation ) {
-                                       char tmpDirPath[64];
-                                       const char* pathLastSlash = strrchr(fCacheFilePath, '/');
-                                       if ( pathLastSlash != NULL ) {
-                                               sprintf(tmpDirPath, "/var/run%s.old.%u", pathLastSlash, getpid());
-                                               // move existing cache file to /var/run to be clean up next boot
-                                               result = ::rename(fCacheFilePath, tmpDirPath);
-                                               if ( result != 0 ) {
-                                                       if ( errno != ENOENT )
-                                                               fprintf(stderr, "update_dyld_shared_cache: warning, unable to move existing cache to %s errno=%d for %s\n", tmpDirPath, errno, fCacheFilePath);
-                                               }
-                                       }
-                               }
-                               
-                               // move new cache file to correct location for use after reboot
-                               if ( verbose )
-                                       fprintf(stderr, "update_dyld_shared_cache: atomically moving cache file into place: %s\n", fCacheFilePath);
-                               result = ::rename(tempCachePath, fCacheFilePath);
-                               if ( result != 0 ) 
-                                       throwf("can't swap newly create dyld shared cache file: rename(%s,%s) returned errno=%d", tempCachePath, fCacheFilePath, errno);
-                               
-                               
-                               // flush everything to disk to assure rename() gets recorded
-                               sync_volume(fCacheFilePath);
+                               ((dyldCacheHeader<E>*)inMemoryCache)->set_cacheType(0);
+                               writeCacheFile(fCacheFilePath, inMemoryCache, cacheFileSize, fCacheFileInFinalLocation);
                                didUpdate = true;
-                               
-                               // restore default signal handlers
-                               ::signal(SIGINT, SIG_DFL);
-                               ::signal(SIGBUS, SIG_DFL);
-                               ::signal(SIGSEGV, SIG_DFL);
-
                                // generate human readable "map" file that shows the layout of the cache file
                                if ( verbose )
                                        fprintf(stderr, "update_dyld_shared_cache: writing .map file to disk\n");
@@ -3036,7 +3433,7 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                sprintf(tempMapFilePath, "%s.map%u", fCacheFilePath, getpid());
                                FILE* fmap = ::fopen(tempMapFilePath, "w");     
                                if ( fmap == NULL ) {
-                                       fprintf(stderr, "can't create map file %s, errnor=%d", tempCachePath, errno);
+                                       fprintf(stderr, "can't create map file %s, errno=%d", tempMapFilePath, errno);
                                }
                                else {
                                        for(std::vector<shared_file_mapping_np>::iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
@@ -3135,7 +3532,7 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                                                }
                                        }
                                        fclose(fmap);
-                                       result = ::rename(tempMapFilePath, mapFilePath);
+                                       ::rename(tempMapFilePath, mapFilePath);
                                }
                        }
                        
@@ -3148,8 +3545,6 @@ bool SharedCache<A>::update(bool force, bool optimize, bool deleteExistingFirst,
                        }
                }
                catch (...){
-                       // remove temp cache file
-                       ::unlink(tempCachePath);
                        // remove in memory cache
                        if ( inMemoryCache != NULL ) 
                                vm_deallocate(mach_task_self(), (vm_address_t)inMemoryCache, allocatedCacheSize);
diff --git a/launch-cache/update_dyld_shared_cache_entitlements.plist b/launch-cache/update_dyld_shared_cache_entitlements.plist
new file mode 100644 (file)
index 0000000..5ed9d1c
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+    <dict>
+      <key>com.apple.rootless.storage.dyld</key>
+      <true/>
+    </dict>
+</plist>
diff --git a/libdyld.xcconfig b/libdyld.xcconfig
deleted file mode 100644 (file)
index 649af26..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-LIBSYSTEM_LIBS[sdk=*simulator*]     = -Wl,-upward-lsystem_sim_c  -Wl,-upward-lSystem
-LIBSYSTEM_LIBS[sdk=iphoneos*]       = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-llaunch -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel
-LIBSYSTEM_LIBS[sdk=macosx*]         = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-llaunch -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel
-
-INSTALL_PATH_ACTUAL = /usr/lib/system
-
-
index 5b297f36c93aed7a95bed83c7b3a8d1b9392a4b3..657677a758eb502e3c4ea841c8fae40ac51515ab 100644 (file)
@@ -883,11 +883,10 @@ void ImageLoader::weakBind(const LinkContext& context)
                                                }
                                        }
                                }
-                               if ( context.verboseWeakBind )
-                                       dyld::log("dyld: weak binding all uses of %s to copy from %s\n", nameToCoalesce, targetImage->getShortName());
-                               
                                // tell each to bind to this symbol (unless already bound)
                                if ( targetAddr != 0 ) {
+                                       if ( context.verboseWeakBind )
+                                               dyld::log("dyld: weak binding all uses of %s to copy from %s\n", nameToCoalesce, targetImage->getShortName());
                                        for(int i=0; i < count; ++i) {
                                                if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) {
                                                        if ( context.verboseWeakBind )
index 6f4c3bc6f14b2623771b035212135676ec2329f7..877044cbf4f8bbe98f9c03bd8faeb747e25677fc 100644 (file)
        #define TEXT_RELOC_SUPPORT                              __i386__
        #define DYLD_SHARED_CACHE_SUPPORT               (__arm__ || __arm64__)
        #define SUPPORT_OLD_CRT_INITIALIZATION  0
-       #define SUPPORT_LC_DYLD_ENVIRONMENT             0
-       #define SUPPORT_VERSIONED_PATHS                 0
+       #define SUPPORT_LC_DYLD_ENVIRONMENT             1
+       #define SUPPORT_VERSIONED_PATHS                 1
        #define SUPPORT_CLASSIC_MACHO                   __arm__
        #define SUPPORT_ZERO_COST_EXCEPTIONS    (!__USING_SJLJ_EXCEPTIONS__)
        #define INITIAL_IMAGE_COUNT                             256
@@ -291,6 +291,7 @@ public:
                bool                    linkingMainExecutable;
                bool                    startedInitializingMainExecutable;
                bool                    processIsRestricted;
+               bool                    processRequiresLibraryValidation;
                bool                    verboseOpts;
                bool                    verboseEnv;
                bool                    verboseMapping;
index 53b0ddd1b21385c14bfead0af01173596d3d1f62..d552ef3bf06015f055843ad5cdb984fe0ce5e925 100644 (file)
@@ -60,6 +60,15 @@ extern "C" long __stack_chk_guard;
        #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
 #endif
 
+#ifndef LC_VERSION_MIN_TVOS
+       #define LC_VERSION_MIN_TVOS 0x2F
+#endif
+
+#ifndef LC_VERSION_MIN_WATCHOS
+       #define LC_VERSION_MIN_WATCHOS 0x30
+#endif
+
+
 // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
 #if __LP64__
        #define LC_SEGMENT_COMMAND              LC_SEGMENT_64
@@ -83,9 +92,9 @@ uint32_t ImageLoaderMachO::fgSymbolTrieSearchs = 0;
 
 ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, 
                                                                                                                                uint32_t segOffsets[], unsigned int libCount)
- : ImageLoader(path, libCount), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), 
+ : ImageLoader(path, libCount), fCoveredCodeLength(0), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0),
        fEHFrameSectionOffset(0), fUnwindInfoSectionOffset(0), fDylibIDOffset(0), 
-       fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false),  
+fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false),
 #if TEXT_RELOC_SUPPORT
        fTextSegmentRebases(false),
        fTextSegmentBinds(false),
@@ -119,26 +128,25 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns
 
 
 // determine if this mach-o file has classic or compressed LINKEDIT and number of segments it has
-void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed, 
+void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed,
                                                                                        unsigned int* segCount, unsigned int* libCount, const LinkContext& context,
-                                                                                       const linkedit_data_command** codeSigCmd)
+                                                                                       const linkedit_data_command** codeSigCmd,
+                                                                                       const encryption_info_command** encryptCmd)
 {
        *compressed = false;
        *segCount = 0;
        *libCount = 0;
        *codeSigCmd = NULL;
-       struct macho_segment_command* segCmd;
-       bool foundLoadCommandSegment = false;
-       uint32_t loadCommandSegmentIndex = 0xFFFFFFFF;
-       uintptr_t loadCommandSegmentVMStart = 0;
-       uintptr_t loadCommandSegmentVMEnd = 0;
+       *encryptCmd = NULL;
 
        const uint32_t cmd_count = mh->ncmds;
        const struct load_command* const startCmds    = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header));
        const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + mh->sizeofcmds);
        const struct load_command* cmd = startCmds;
+       bool foundLoadCommandSegment = false;
        for (uint32_t i = 0; i < cmd_count; ++i) {
                uint32_t cmdLength = cmd->cmdsize;
+               struct macho_segment_command* segCmd;
                if ( cmdLength < 8 ) {
                        dyld::throwf("malformed mach-o image: load command #%d length (%u) too small in %s",
                                                                                           i, cmdLength, path);
@@ -155,25 +163,56 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
                                break;
                        case LC_SEGMENT_COMMAND:
                                segCmd = (struct macho_segment_command*)cmd;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+                               // rdar://problem/19617624 allow unmapped segments on OSX (but not iOS)
+                               if ( (segCmd->filesize > segCmd->vmsize) && (segCmd->vmsize != 0) )
+#else
+                               if ( segCmd->filesize > segCmd->vmsize )
+#endif
+                                   dyld::throwf("malformed mach-o image: segment load command %s filesize is larger than vmsize", segCmd->segname);
                                // ignore zero-sized segments
                                if ( segCmd->vmsize != 0 )
                                        *segCount += 1;
-                               // <rdar://problem/7942521> all load commands must be in an executable segment
-                               if ( context.codeSigningEnforced && (segCmd->fileoff < mh->sizeofcmds) && (segCmd->filesize != 0) ) {
-                                       if ( (segCmd->fileoff != 0) || (segCmd->filesize < (mh->sizeofcmds+sizeof(macho_header))) ) 
-                                               dyld::throwf("malformed mach-o image: segment %s does not span all load commands", segCmd->segname); 
-                                       if ( segCmd->initprot != (VM_PROT_READ | VM_PROT_EXECUTE) ) 
-                                               dyld::throwf("malformed mach-o image: load commands found in segment %s with wrong permissions", segCmd->segname); 
-                                       if ( foundLoadCommandSegment )
-                                               throw "load commands in multiple segments";
-                                       foundLoadCommandSegment = true;
-                                       loadCommandSegmentIndex = i;
-                                       loadCommandSegmentVMStart = segCmd->vmaddr;
-                                       loadCommandSegmentVMEnd   = segCmd->vmaddr + segCmd->vmsize;
-                                       if ( (intptr_t)(segCmd->vmsize) < 0)
-                                               dyld::throwf("malformed mach-o image: segment load command %s size too large", segCmd->segname);
-                                       if ( loadCommandSegmentVMEnd < loadCommandSegmentVMStart )
+                               if ( context.codeSigningEnforced ) {
+                                       uintptr_t vmStart   = segCmd->vmaddr;
+                                       uintptr_t vmSize    = segCmd->vmsize;
+                                       uintptr_t vmEnd     = vmStart + vmSize;
+                                       uintptr_t fileStart = segCmd->fileoff;
+                                       uintptr_t fileSize  = segCmd->filesize;
+                                       if ( (intptr_t)(vmEnd) < 0)
+                                               dyld::throwf("malformed mach-o image: segment load command %s vmsize too large", segCmd->segname);
+                                       if ( vmStart > vmEnd )
                                                dyld::throwf("malformed mach-o image: segment load command %s wraps around address space", segCmd->segname);
+                                       if ( vmSize != fileSize ) {
+                                               if ( (segCmd->initprot == 0) && (fileSize != 0) )
+                                                       dyld::throwf("malformed mach-o image: unaccessable segment %s has filesize != 0", segCmd->segname);
+                                               else if ( vmSize < fileSize )
+                                                       dyld::throwf("malformed mach-o image: segment %s has vmsize < filesize", segCmd->segname);
+                                       }
+                                       if ( inCache ) {
+                                               if ( (fileSize != 0) && (segCmd->initprot == (VM_PROT_READ | VM_PROT_EXECUTE)) ) {
+                                                       if ( foundLoadCommandSegment )
+                                                               throw "load commands in multiple segments";
+                                                       foundLoadCommandSegment = true;
+                                               }
+                                       }
+                                       else if ( (fileStart < mh->sizeofcmds) && (fileSize != 0) ) {
+                                               // <rdar://problem/7942521> all load commands must be in an executable segment
+                                               if ( (fileStart != 0) || (fileSize < (mh->sizeofcmds+sizeof(macho_header))) )
+                                                       dyld::throwf("malformed mach-o image: segment %s does not span all load commands", segCmd->segname); 
+                                               if ( segCmd->initprot != (VM_PROT_READ | VM_PROT_EXECUTE) ) 
+                                                       dyld::throwf("malformed mach-o image: load commands found in segment %s with wrong permissions", segCmd->segname); 
+                                               if ( foundLoadCommandSegment )
+                                                       throw "load commands in multiple segments";
+                                               foundLoadCommandSegment = true;
+                                       }
+
+                                       const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command));
+                                       const struct macho_section* const sectionsEnd = &sectionsStart[segCmd->nsects];
+                                       for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+                                               if (!inCache && sect->offset != 0 && ((sect->offset + sect->size) > (segCmd->fileoff + segCmd->filesize)))
+                                                       dyld::throwf("malformed mach-o image: section %s,%s of '%s' exceeds segment %s booundary", sect->segname, sect->sectname, path, segCmd->segname);
+                                       }
                                }
                                break;
                        case LC_SEGMENT_COMMAND_WRONG:
@@ -188,36 +227,68 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
                        case LC_CODE_SIGNATURE:
                                *codeSigCmd = (struct linkedit_data_command*)cmd; // only support one LC_CODE_SIGNATURE per image
                                break;
+                       case LC_ENCRYPTION_INFO:
+                       case LC_ENCRYPTION_INFO_64:
+                               *encryptCmd = (struct encryption_info_command*)cmd; // only support one LC_ENCRYPTION_INFO[_64] per image
+                               break;
                }
                cmd = nextCmd;
        }
-       
+
        if ( context.codeSigningEnforced && !foundLoadCommandSegment )
                throw "load commands not in a segment";
-       // <rdar://problem/13145644> verify another segment does not over-map load commands
-       cmd = startCmds;
+
+       // <rdar://problem/13145644> verify every segment does not overlap another segment
        if ( context.codeSigningEnforced ) {
+               uintptr_t lastFileStart = 0;
+               uintptr_t linkeditFileStart = 0;
+               const struct load_command* cmd1 = startCmds;
                for (uint32_t i = 0; i < cmd_count; ++i) {
-                       switch (cmd->cmd) {
-                               case LC_SEGMENT_COMMAND:
-                                       if ( i != loadCommandSegmentIndex ) {
-                                               segCmd = (struct macho_segment_command*)cmd;
-                                               uintptr_t start = segCmd->vmaddr;
-                                               uintptr_t end = segCmd->vmaddr + segCmd->vmsize;
-                                               if ( ((start <= loadCommandSegmentVMStart) && (end > loadCommandSegmentVMStart)) 
-                                                  || ((start >= loadCommandSegmentVMStart) && (start < loadCommandSegmentVMEnd)) )
-                                                       dyld::throwf("malformed mach-o image: segment %s overlaps load commands", segCmd->segname); 
+                       if ( cmd1->cmd == LC_SEGMENT_COMMAND ) {
+                               struct macho_segment_command* segCmd1 = (struct macho_segment_command*)cmd1;
+                               uintptr_t vmStart1   = segCmd1->vmaddr;
+                               uintptr_t vmEnd1     = segCmd1->vmaddr + segCmd1->vmsize;
+                               uintptr_t fileStart1 = segCmd1->fileoff;
+                               uintptr_t fileEnd1   = segCmd1->fileoff + segCmd1->filesize;
+
+                               if (fileStart1 > lastFileStart)
+                                       lastFileStart = fileStart1;
+
+                               if ( strcmp(&segCmd1->segname[0], "__LINKEDIT") == 0 ) {
+                                       linkeditFileStart = fileStart1;
+                               }
+
+                               const struct load_command* cmd2 = startCmds;
+                               for (uint32_t j = 0; j < cmd_count; ++j) {
+                                       if ( cmd2 == cmd1 )
+                                               continue;
+                                       if ( cmd2->cmd == LC_SEGMENT_COMMAND ) {
+                                               struct macho_segment_command* segCmd2 = (struct macho_segment_command*)cmd2;
+                                               uintptr_t vmStart2   = segCmd2->vmaddr;
+                                               uintptr_t vmEnd2     = segCmd2->vmaddr + segCmd2->vmsize;
+                                               uintptr_t fileStart2 = segCmd2->fileoff;
+                                               uintptr_t fileEnd2   = segCmd2->fileoff + segCmd2->filesize;
+                                               if ( ((vmStart2 <= vmStart1) && (vmEnd2 > vmStart1) && (vmEnd1 > vmStart1)) 
+                                               || ((vmStart2 >= vmStart1) && (vmStart2 < vmEnd1) && (vmEnd2 > vmStart2)) )
+                                                       dyld::throwf("malformed mach-o image: segment %s vm overlaps segment %s", segCmd1->segname, segCmd2->segname);
+                                               if ( ((fileStart2 <= fileStart1) && (fileEnd2 > fileStart1) && (fileEnd1 > fileStart1))
+                                                 || ((fileStart2 >= fileStart1) && (fileStart2 < fileEnd1) && (fileEnd2 > fileStart2)) )
+                                                       dyld::throwf("malformed mach-o image: segment %s file content overlaps segment %s", segCmd1->segname, segCmd2->segname); 
                                        }
-                                       break;
+                                       cmd2 = (const struct load_command*)(((char*)cmd2)+cmd2->cmdsize);
+                               }
                        }
-                       cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+                       cmd1 = (const struct load_command*)(((char*)cmd1)+cmd1->cmdsize);
                }
+
+               if (lastFileStart != linkeditFileStart)
+                       dyld::throwf("malformed mach-o image: __LINKEDIT must be last segment");
        }
-       
+
        // fSegmentsArrayCount is only 8-bits
        if ( *segCount > 255 )
                dyld::throwf("malformed mach-o image: more than 255 segments in %s", path);
-               
+
        // fSegmentsArrayCount is only 8-bits
        if ( *libCount > 4095 )
                dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path);
@@ -237,7 +308,8 @@ ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh,
        unsigned int segCount;
        unsigned int libCount;
        const linkedit_data_command* codeSigCmd;
-       sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, context, &codeSigCmd);
+       const encryption_info_command* encryptCmd;
+       sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
        // instantiate concrete class based on content of load commands
        if ( compressed ) 
                return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context);
@@ -269,13 +341,14 @@ ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, con
        unsigned int segCount;
        unsigned int libCount;
        const linkedit_data_command* codeSigCmd;
-       sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount, context, &codeSigCmd);
+       const encryption_info_command* encryptCmd;
+       sniffLoadCommands((const macho_header*)fileData, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
        // instantiate concrete class based on content of load commands
        if ( compressed ) 
-               return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context);
+               return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, dataSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, encryptCmd, context);
        else
 #if SUPPORT_CLASSIC_MACHO
-               return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context);
+               return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, dataSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context);
 #else
                throw "missing LC_DYLD_INFO load command";
 #endif
@@ -289,7 +362,8 @@ ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, cons
        unsigned int segCount;
        unsigned int libCount;
        const linkedit_data_command* codeSigCmd;
-       sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, context, &codeSigCmd);
+       const encryption_info_command* encryptCmd;
+       sniffLoadCommands(mh, path, true, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd);
        // instantiate concrete class based on content of load commands
        if ( compressed ) 
                return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context);
@@ -308,7 +382,8 @@ ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, con
        unsigned int segCount;
        unsigned int libCount;
        const linkedit_data_command* sigcmd;
-       sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount, context, &sigcmd);
+       const encryption_info_command* encryptCmd;
+       sniffLoadCommands(mh, moduleName, false, &compressed, &segCount, &libCount, context, &sigcmd, &encryptCmd);
        // instantiate concrete class based on content of load commands
        if ( compressed ) 
                return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context);
@@ -337,13 +412,16 @@ int ImageLoaderMachO::crashIfInvalidCodeSignature()
 }
 
 
-void ImageLoaderMachO::parseLoadCmds()
+void ImageLoaderMachO::parseLoadCmds(const LinkContext& context)
 {
        // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide
        for(unsigned int i=0; i < fSegmentsCount; ++i) {
                // set up pointer to __LINKEDIT segment
-               if ( strcmp(segName(i),"__LINKEDIT") == 0 ) 
+               if ( strcmp(segName(i),"__LINKEDIT") == 0 ) {
+                       if ( context.codeSigningEnforced && (segFileOffset(i) > fCoveredCodeLength))
+                               dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName());
                        fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i));
+               }
 #if TEXT_RELOC_SUPPORT
                // __TEXT segment always starts at beginning of file and contains mach_header and load commands
                if ( segExecutable(i) ) {
@@ -451,6 +529,8 @@ void ImageLoaderMachO::parseLoadCmds()
                                break;
                        case LC_VERSION_MIN_MACOSX:
                        case LC_VERSION_MIN_IPHONEOS:
+                       case LC_VERSION_MIN_TVOS:
+                       case LC_VERSION_MIN_WATCHOS:
                                minOSVersionCmd = (version_min_command*)cmd;
                                break;
                        default:
@@ -792,6 +872,8 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod
                        dyld::throwf("required code signature missing for '%s'\n", this->getPath());
                }
 #endif
+               //Since we don't have a range for the signature we have to assume full coverage
+               fCoveredCodeLength = UINT64_MAX;
        }
        else {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
@@ -804,15 +886,54 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod
                siginfo.fs_file_start=offsetInFatFile;                          // start of mach-o slice in fat file 
                siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff);       // start of CD in mach-o file
                siginfo.fs_blob_size=codeSigCmd->datasize;                      // size of CD
-               int result = fcntl(fd, F_ADDFILESIGS, &siginfo);
+               int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
+
+#if TARGET_IPHONE_SIMULATOR
+               // rdar://problem/18759224> check range covered by the code directory after loading
+               // Attempt to fallback only if we are in the simulator
+
+               if ( result == -1 ) {
+                       result = fcntl(fd, F_ADDFILESIGS, &siginfo);
+                       siginfo.fs_file_start = codeSigCmd->dataoff;
+               }
+#endif
+
                if ( result == -1 ) {
                        if ( (errno == EPERM) || (errno == EBADEXEC) )
                                dyld::throwf("code signature invalid for '%s'\n", this->getPath());
                        if ( context.verboseCodeSignatures ) 
                                dyld::log("dyld: Failed registering code signature for %s, errno=%d\n", this->getPath(), errno);
+                       siginfo.fs_file_start = UINT64_MAX;
+               } else if ( context.verboseCodeSignatures )  {
+                       dyld::log("dyld: Registered code signature for %s\n", this->getPath());
+               }
+               fCoveredCodeLength = siginfo.fs_file_start;
+       }
+}
+
+void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context)
+{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       // rdar://problem/21839703> 15A226d: dyld crashes in mageLoaderMachO::validateFirstPages during dlopen() after encountering an mmap failure
+       // We need to ignore older code signatures because they will be bad.
+       if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) {
+               return;
+       }
+#endif
+       if (codeSigCmd != NULL) {
+               if ( context.verboseMapping )
+                       dyld::log("dyld: validate pages: %llu\n", (unsigned long long)offsetInFat);
+
+               void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, MAP_SHARED, fd, offsetInFat);
+               if ( fdata == MAP_FAILED ) {
+                       if ( context.processRequiresLibraryValidation )
+                               dyld::throwf("cannot load image with wrong team ID in process using Library Validation");
                        else
-                               dyld::log("dyld: Registered code signature for %s\n", this->getPath());
+                               dyld::throwf("mmap() errno=%d validating first page of '%s'", errno, getInstallPath());
                }
+               if ( memcmp(fdata, fileData, lenFileData) != 0 )
+                       dyld::throwf("mmap() page compare failed for '%s'", getInstallPath());
+               munmap(fdata, lenFileData);
        }
 }
 
@@ -880,6 +1001,8 @@ uint32_t ImageLoaderMachO::sdkVersion() const
                switch ( cmd->cmd ) {
                        case LC_VERSION_MIN_MACOSX:
                        case LC_VERSION_MIN_IPHONEOS:
+                       case LC_VERSION_MIN_TVOS:
+                       case LC_VERSION_MIN_WATCHOS:
                                versCmd = (version_min_command*)cmd;
                                return versCmd->sdk;
                }
@@ -898,6 +1021,8 @@ uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh)
                switch ( cmd->cmd ) {
                        case LC_VERSION_MIN_MACOSX:
                        case LC_VERSION_MIN_IPHONEOS:
+                       case LC_VERSION_MIN_TVOS:
+                       case LC_VERSION_MIN_WATCHOS:
                                versCmd = (version_min_command*)cmd;
                                return versCmd->version;
                }
@@ -1077,7 +1202,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector<const c
                                const char* pathToAdd = NULL;
                                const char* path = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
                                if ( (strncmp(path, "@loader_path", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) {
-                                       if ( context.processIsRestricted  && (context.mainExecutable == this) ) {
+                                       if ( context.processIsRestricted && !context.processRequiresLibraryValidation && (context.mainExecutable == this) ) {
                                                dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath());
                                                break;
                                        }
@@ -1093,7 +1218,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector<const c
                                        }
                                }
                                else if ( (strncmp(path, "@executable_path", 16) == 0) && ((path[16] == '/') || (path[16] == '\0')) ) {
-                                       if ( context.processIsRestricted ) {
+                                       if ( context.processIsRestricted && !context.processRequiresLibraryValidation ) {
                                                dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @executable_path\n", path, this->getPath());
                                                break;
                                        }
@@ -1108,7 +1233,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector<const c
                                                }
                                        }
                                }
-                               else if ( (path[0] != '/') && context.processIsRestricted ) {
+                               else if ( (path[0] != '/') && context.processIsRestricted && !context.processRequiresLibraryValidation ) {
                                        dyld::warn("LC_RPATH %s in %s being ignored in restricted program because it is a relative path\n", path, this->getPath());
                                        break;
                                }
@@ -1543,13 +1668,13 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context)
                for (uint32_t i = 0; i < cmd_count; ++i) {
                        if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
                                const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
-                               if ( strcmp(seg->segname, "__DATA") == 0 ) {
+                               if ( strncmp(seg->segname, "__DATA", 6) == 0 ) {
                                        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) {
                                                if ( strcmp(sect->sectname, "__dyld" ) == 0 ) {
                                                        struct DATAdyld* dd = (struct DATAdyld*)(sect->addr + fSlide);
-                               #if !__arm64__
+                               #if !__arm64__ && !__ARM_ARCH_7K__
                                                        if ( sect->size > offsetof(DATAdyld, dyldLazyBinder) ) {
                                                                if ( dd->dyldLazyBinder != (void*)&stub_binding_helper )
                                                                        dd->dyldLazyBinder = (void*)&stub_binding_helper;
index 4505d63990b58ad8f70caeefe4d3952d8b92f0be..72c90f1e425b7a15e49d7bb978a7246e0c8a7836 100644 (file)
 class ImageLoaderMachO : public ImageLoader {
 public:
        static ImageLoader*                                     instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context);
-       static ImageLoader*                                     instantiateFromFile(const char* path, int fd, const uint8_t firstPage[4096], uint64_t offsetInFat, 
+       static ImageLoader*                                     instantiateFromFile(const char* path, int fd, const uint8_t firstPages[4096], uint64_t offsetInFat,
                                                                                                                        uint64_t lenInFat, const struct stat& info, const LinkContext& context);
        static ImageLoader*                                     instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context);
        static ImageLoader*                                     instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context);
 
 
        bool                                                            inSharedCache() const { return fInSharedCache; }
+       void                                                            disableCoverageCheck() { fCoveredCodeLength = UINT64_MAX; }
+
        const char*                                                     getInstallPath() const;
        virtual void*                                           getMain() const;
        virtual void*                                           getThreadPC() const;
@@ -156,13 +158,15 @@ protected:
 protected:
 
                        void            destroy();
-       static void                     sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed,
+       static void                     sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed,
                                                                                        unsigned int* segCount, unsigned int* libCount, const LinkContext& context,
-                                                                                       const linkedit_data_command** codeSigCmd);
+                                                                                       const linkedit_data_command** codeSigCmd,
+                                                                                       const encryption_info_command** encryptCmd);
        static bool                     needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh);
                        void            loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context);
+                       void            validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context);
                        const struct macho_segment_command* segLoadCommand(unsigned int segIndex) const;
-                       void            parseLoadCmds();
+                       void            parseLoadCmds(const ImageLoader::LinkContext& context);
                        int             crashIfInvalidCodeSignature();
                        bool            segHasRebaseFixUps(unsigned int) const;
                        bool            segHasBindFixUps(unsigned int) const;
@@ -198,7 +202,8 @@ protected:
                                                                                const LinkContext& context, bool runResolver) const;
                        
        static uintptr_t                        bindLazySymbol(const mach_header*, uintptr_t* lazyPointer);
-protected:                     
+protected:
+       uint64_t                                                                fCoveredCodeLength;
        const uint8_t*                                                  fMachOData;
        const uint8_t*                                                  fLinkEditBase; // add any internal "offset" to this to get mapped address
        uintptr_t                                                               fSlide;
index 8190f1d37629d4a8e825a8407ffe4c5e6b1f9dca..4a0f5b3b4e0f517be097ea4f3896daefc3599511 100644 (file)
@@ -97,7 +97,8 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(cons
        // for PIE record end of program, to know where to start loading dylibs
        if ( slide != 0 )
                fgNextPIEDylibAddress = (uintptr_t)image->getEnd();
-       
+
+       image->disableCoverageCheck();
        image->instantiateFinish(context);
        image->setMapped(context);
 
@@ -126,7 +127,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(cons
 }
 
 // create image by mapping in a mach-o file
-ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, 
+ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData,
                                                                                                                        uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, 
                                                                                                                        unsigned int segCount, unsigned int libCount, 
                                                                                                                        const struct linkedit_data_command* codeSigCmd, const LinkContext& context)
@@ -139,6 +140,9 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char
                // if this image is code signed, let kernel validate signature before mapping any pages from image
                image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context);
                
+               // Validate that first data we read with pread actually matches with code signature
+               image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context);
+
                // mmap segments
                image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context);
 
@@ -192,6 +196,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const mac
                // remember this is from shared cache and cannot be unloaded
                image->fInSharedCache = true;
                image->setNeverUnload();
+               image->disableCoverageCheck();
 
                // segments already mapped in cache
                if ( context.verboseMapping ) {
@@ -230,6 +235,8 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromMemory(const ch
                // for compatibility, never unload dylibs loaded from memory
                image->setNeverUnload();
 
+               image->disableCoverageCheck();
+
                // bundle loads need path copied
                if ( moduleName != NULL ) 
                        image->setPath(moduleName);
@@ -273,7 +280,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateStart(const macho_h
 void ImageLoaderMachOClassic::instantiateFinish(const LinkContext& context)
 {
        // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide
-       this->parseLoadCmds();
+       this->parseLoadCmds(context);
 }
 
 ImageLoaderMachOClassic::~ImageLoaderMachOClassic()
index 88db64cbe07526921a3dfbb823a78d931c135686..071bea372262edc807f967303ec0fe80eef7a315 100644 (file)
@@ -39,7 +39,7 @@ class ImageLoaderMachOClassic : public ImageLoaderMachO {
 public:
        static ImageLoaderMachOClassic*         instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, 
                                                                                                                                        unsigned int segCount, unsigned int libCount, const LinkContext& context);
-       static ImageLoaderMachOClassic*         instantiateFromFile(const char* path, int fd, const uint8_t* fileData, 
+       static ImageLoaderMachOClassic*         instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, 
                                                                                                                        uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, 
                                                                                                                        unsigned int segCount, unsigned int libCount, 
                                                                                                                        const struct linkedit_data_command* codeSigCmd, const LinkContext& context);
index 6a3a535d28b0395ed889e3fba135782db7d8c317..329b81dbb0acadd97b5eb3d83ae77dc1715bb6ee 100644 (file)
  */
 
 
+#if __arm__ || __arm64__
+  #include <System/sys/mman.h>
+#else
+  #include <sys/mman.h>
+#endif
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/fcntl.h>
 #include <sys/stat.h> 
-#include <sys/mman.h>
 #include <sys/param.h>
 #include <mach/mach.h>
 #include <mach/thread_status.h>
 #include <mach-o/loader.h> 
-
 #include "ImageLoaderMachOCompressed.h"
 #include "mach-o/dyld_images.h"
 
@@ -112,10 +115,11 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutabl
        // for PIE record end of program, to know where to start loading dylibs
        if ( slide != 0 )
                fgNextPIEDylibAddress = (uintptr_t)image->getEnd();
-       
+
+       image->disableCoverageCheck();
        image->instantiateFinish(context);
        image->setMapped(context);
-       
+
        if ( context.verboseMapping ) {
                dyld::log("dyld: Main executable mapped %s\n", path);
                for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) {
@@ -131,10 +135,12 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutabl
 }
 
 // create image by mapping in a mach-o file
-ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, 
+ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData,
                                                                                                                        uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, 
                                                                                                                        unsigned int segCount, unsigned int libCount, 
-                                                                                                                       const struct linkedit_data_command* codeSigCmd, const LinkContext& context)
+                                                                                                                       const struct linkedit_data_command* codeSigCmd, 
+                                                                                                                       const struct encryption_info_command* encryptCmd, 
+                                                                                                                       const LinkContext& context)
 {
        ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount);
 
@@ -145,9 +151,15 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons
                // if this image is code signed, let kernel validate signature before mapping any pages from image
                image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context);
                
+               // Validate that first data we read with pread actually matches with code signature
+               image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context);
+
                // mmap segments
                image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context);
 
+               // if framework is FairPlay encrypted, register with kernel
+               image->registerEncryption(encryptCmd, context);
+               
                // probe to see if code signed correctly
                image->crashIfInvalidCodeSignature();
 
@@ -209,6 +221,7 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(con
                image->fInSharedCache = true;
                image->setNeverUnload();
                image->setSlide(slide);
+               image->disableCoverageCheck();
 
                // segments already mapped in cache
                if ( context.verboseMapping ) {
@@ -247,6 +260,8 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(co
                // for compatibility, never unload dylibs loaded from memory
                image->setNeverUnload();
 
+               image->disableCoverageCheck();
+
                // bundle loads need path copied
                if ( moduleName != NULL ) 
                        image->setPath(moduleName);
@@ -297,7 +312,7 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const m
 void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context)
 {
        // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide
-       this->parseLoadCmds();
+       this->parseLoadCmds(context);
 }
 
 uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const
@@ -1692,3 +1707,32 @@ void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext&
 }
 
 
+void ImageLoaderMachOCompressed::registerEncryption(const encryption_info_command* encryptCmd, const LinkContext& context)
+{
+#if __arm__ || __arm64__
+       if ( encryptCmd == NULL )
+               return;
+       const mach_header* mh = NULL;
+       for(unsigned int i=0; i < fSegmentsCount; ++i) {
+               if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) {
+                       mh = (mach_header*)segActualLoadAddress(i);
+                       break;
+               }
+       }
+       void* start = ((uint8_t*)mh) + encryptCmd->cryptoff;
+       size_t len = encryptCmd->cryptsize;
+       uint32_t cputype = mh->cputype;
+       uint32_t cpusubtype = mh->cpusubtype;
+       uint32_t cryptid = encryptCmd->cryptid;
+       if (context.verboseMapping) {
+                dyld::log("                      0x%08lX->0x%08lX configured for FairPlay decryption\n", (long)start, (long)start+len);
+       }
+       int result = mremap_encrypted(start, len, cryptid, cputype, cpusubtype);
+       if ( result != 0 ) {
+               dyld::throwf("mremap_encrypted() => %d, errno=%d for %s\n", result, errno, this->getPath());
+       }
+#endif
+}
+
+
+
index 1a43900d574a7050a28c922ef654f7b913c9d7ed..e655adcf84f3eea86f088f04cfe87fcc2a6bdaf0 100644 (file)
@@ -39,10 +39,12 @@ class ImageLoaderMachOCompressed : public ImageLoaderMachO {
 public:
        static ImageLoaderMachOCompressed*      instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, 
                                                                                                                                        unsigned int segCount, unsigned int libCount, const LinkContext& context);
-       static ImageLoaderMachOCompressed*      instantiateFromFile(const char* path, int fd, const uint8_t* fileData, 
+       static ImageLoaderMachOCompressed*      instantiateFromFile(const char* path, int fd, const uint8_t *fileData, size_t lenFileData,
                                                                                                                        uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, 
                                                                                                                        unsigned int segCount, unsigned int libCount, 
-                                                                                                                       const struct linkedit_data_command* codeSigCmd, const LinkContext& context);
+                                                                                                                       const struct linkedit_data_command* codeSigCmd, 
+                                                                                                                       const struct encryption_info_command* encryptCmd,
+                                                                                                                       const LinkContext& context);
        static ImageLoaderMachOCompressed*      instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info,
                                                                                                                                unsigned int segCount, unsigned int libCount, const LinkContext& context);
        static ImageLoaderMachOCompressed*      instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, 
@@ -134,7 +136,8 @@ private:
        static const uint8_t*                           trieWalk(const uint8_t* start, const uint8_t* end, const  char* s);
     void                                updateOptimizedLazyPointers(const LinkContext& context);
     void                                updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context);
-               
+       void                                                            registerEncryption(const struct encryption_info_command* encryptCmd, const LinkContext& context);
+       
        const struct dyld_info_command*                 fDyldInfo;
 };
 
index b34447f6309253ff74303af286e714a8753f2a7f..648b8873bd7a1e7588efaa769650fe16083fcaca 100644 (file)
@@ -50,6 +50,7 @@
 #include <libkern/OSAtomic.h>
 #include <Availability.h>
 #include <System/sys/codesign.h>
+#include <System/sys/csr.h>
 #include <_simple.h>
 #include <os/lock_private.h>
 
 #if DYLD_SHARED_CACHE_SUPPORT
 #include "dyld_cache_format.h"
 #endif
+#include <coreSymbolicationDyldSupport.h>
 #if TARGET_IPHONE_SIMULATOR
-  void coresymbolication_load_image(void*, const ImageLoader*, uint64_t);
-  void coresymbolication_unload_image(void*, const ImageLoader*);
-#else
-  #include "coreSymbolicationDyldSupport.hpp"
+       extern "C" void xcoresymbolication_load_notifier(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header);
+       extern "C" void xcoresymbolication_unload_notifier(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
+       #define coresymbolication_load_notifier(c, t, p, h) xcoresymbolication_load_notifier(c, t, p, h)
+       #define coresymbolication_unload_notifier(c, t, p, h) xcoresymbolication_unload_notifier(c, t, p, h)
 #endif
 
 // not libc header for send() syscall interface
@@ -111,6 +113,18 @@ extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockadd
 // ARM and x86_64 are the only architecture that use cpu-sub-types
 #define CPU_SUBTYPES_SUPPORTED  ((__arm__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR)
 
+#if __LP64__
+       #define LC_SEGMENT_COMMAND              LC_SEGMENT_64
+       #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT
+       #define macho_segment_command   segment_command_64
+       #define macho_section                   section_64
+#else
+       #define LC_SEGMENT_COMMAND              LC_SEGMENT
+       #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64
+       #define macho_segment_command   segment_command
+       #define macho_section                   section
+#endif
+
 
 
 #define CPU_TYPE_MASK 0x00FFFFFF       /* complement of CPU_ARCH_MASK */
@@ -219,6 +233,7 @@ static cpu_subtype_t                                sHostCPUsubtype;
 #endif
 static ImageLoader*                                    sMainExecutable = NULL;
 static bool                                                    sProcessIsRestricted = false;
+static bool                                                    sProcessRequiresLibraryValidation = false;
 static RestrictedReason                                sRestrictedReason = restrictedNot;
 static size_t                                          sInsertedDylibCount = 0;
 static std::vector<ImageLoader*>       sAllImages;
@@ -232,8 +247,13 @@ static void*                                               sSingleHandlers[7][3];
 static void*                                           sBatchHandlers[7][3];
 static ImageLoader*                                    sLastImageByAddressCache;
 static EnvironmentVariables                    sEnv;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
 static const char*                                     sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL };
 static const char*                                     sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL };
+#else
+static const char*                                     sFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL };
+static const char*                                     sLibraryFallbackPaths[] = { "/usr/local/lib", "/usr/lib", NULL };
+#endif
 static UndefinedHandler                                sUndefinedHandler = NULL;
 static ImageLoader*                                    sBundleBeingLoaded = NULL;      // hack until OFI is reworked
 #if DYLD_SHARED_CACHE_SUPPORT
@@ -264,6 +284,7 @@ static bool                                                 sFrameworksFoundAsDylibs = false;
 static bool                                                    sHaswell = false;
 #endif
 static std::vector<ImageLoader::DynamicReference> sDynamicReferences;
+static OSSpinLock                                      sDynamicReferencesLock = 0;
 static bool                                                    sLogToFile = false;
 static char                                                    sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries";
 
@@ -509,32 +530,15 @@ static OSSpinLock sAllImagesLock = 0;
 
 static void allImagesLock()
 {
-    //dyld::log("allImagesLock()\n");
-#if TARGET_IPHONE_SIMULATOR    
-       // <rdar://problem/16154256> can't use OSSpinLockLock in simulator until thread_switch is provided by host dyld
-       while ( ! OSAtomicCompareAndSwapPtrBarrier((void*)0, (void*)1, (void**)&sAllImagesLock) ) {
-        // spin
-    }
-#else
        OSSpinLockLock(&sAllImagesLock);
-#endif
 }
 
 static void allImagesUnlock()
 {
-    //dyld::log("allImagesUnlock()\n");
-#if TARGET_IPHONE_SIMULATOR    
-       while ( ! OSAtomicCompareAndSwapPtrBarrier((void*)1, (void*)0, (void**)&sAllImagesLock) ) {
-               // spin
-       }
-#else
        OSSpinLockUnlock(&sAllImagesLock);
-#endif
 }
 
 
-
-
 // utility class to assure files are closed when an exception is thrown
 class FileOpener {
 public:
@@ -699,21 +703,15 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image)
                        }
                }
        }
-    // mach message csdlc about dynamically loaded images 
+    // mach message csdlc about dynamically unloaded images
        if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
+               uint64_t loadTimestamp = mach_absolute_time();
                if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
-                       dyld::log("dyld core symbolication unload notification: %p %s\n", image->machHeader(), image->getPath());
+                       dyld::log("dyld: coresymbolication_unload_notifier(%p, 0x%016llX, %p, %s)\n",
+                                         dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->machHeader(), image->getPath());
                }
                if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) {
-#if TARGET_IPHONE_SIMULATOR
-                       void* connection = dyld::gProcessInfo->coreSymbolicationShmPage;
-                       if ( *((uint32_t*)connection) == 2 ) {
-#else
-                       CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld::gProcessInfo->coreSymbolicationShmPage;
-                       if ( connection->is_valid_version() ) {
-#endif
-                               coresymbolication_unload_image(connection, image);
-                       }
+                       coresymbolication_unload_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->getPath(), image->machHeader());
                }
        }
 }
@@ -823,37 +821,22 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image
                                        }
                                }
                        }
+                       if ( (state == dyld_image_state_dependents_mapped) && ((dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS) ) {
+                               // mach message csdlc about loaded images
+                               uint64_t loadTimestamp = mach_absolute_time();
+                               for (unsigned j=0; j < count; ++j) {
+                                       if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
+                                               dyld::log("dyld: coresymbolication_load_notifier(%p, 0x%016llX, %p, %s)\n",
+                                                                 dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageLoadAddress, infos[j].imageFilePath);
+                                       }
+                                       coresymbolication_load_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageFilePath, infos[j].imageLoadAddress);
+                               }
+                       }
                }
         allImagesUnlock();
         if ( dontLoadReason != NULL )
             throw dontLoadReason;
        }
-       if ( state == dyld_image_state_rebased ) {
-               if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
-                       for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
-                               dyld_image_states imageState = (*it)->getState();
-                               if ( (imageState == dyld_image_state_rebased) || (orLater && (imageState > dyld_image_state_rebased)) )
-                                       dyld::log("dyld core symbolication load notification: %p %s\n", (*it)->machHeader(), (*it)->getPath());
-                       }
-               }
-               if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) {
-#if TARGET_IPHONE_SIMULATOR
-                       void* connection = dyld::gProcessInfo->coreSymbolicationShmPage;
-                       if ( *((uint32_t*)connection) == 2 ) {
-#else
-                       CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld::gProcessInfo->coreSymbolicationShmPage;
-                       if ( connection->is_valid_version() ) {
-#endif
-                               // This needs to be captured now
-                               uint64_t load_timestamp = mach_absolute_time();
-                               for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
-                                       dyld_image_states imageState = (*it)->getState();
-                                       if ( (imageState == state) || (orLater && (imageState > state)) )
-                                               coresymbolication_load_image(connection, *it, load_timestamp);
-                               }
-                       }
-               }
-       }
 }
 
 
@@ -925,15 +908,20 @@ static void addDynamicReference(ImageLoader* from, ImageLoader* to) {
                return;
        
        // don't add if this combination already exists
+       OSSpinLockLock(&sDynamicReferencesLock);
        for (std::vector<ImageLoader::DynamicReference>::iterator it=sDynamicReferences.begin(); it != sDynamicReferences.end(); ++it) {
-               if ( (it->from == from) && (it->to == to) )
+               if ( (it->from == from) && (it->to == to) ) {
+                       OSSpinLockUnlock(&sDynamicReferencesLock);
                        return;
+               }
        }
+
        //dyld::log("addDynamicReference(%s, %s\n", from->getShortName(), to->getShortName());
        ImageLoader::DynamicReference t;
        t.from = from;
        t.to = to;
        sDynamicReferences.push_back(t);
+       OSSpinLockUnlock(&sDynamicReferencesLock);
 }
 
 static void addImage(ImageLoader* image)
@@ -1029,7 +1017,9 @@ void removeImage(ImageLoader* image)
     allImagesUnlock();
        
        // remove from sDynamicReferences
-       sDynamicReferences.erase(std::remove_if(sDynamicReferences.begin(), sDynamicReferences.end(), RefUsesImage(image)), sDynamicReferences.end());
+       OSSpinLockLock(&sDynamicReferencesLock);
+               sDynamicReferences.erase(std::remove_if(sDynamicReferences.begin(), sDynamicReferences.end(), RefUsesImage(image)), sDynamicReferences.end());
+       OSSpinLockUnlock(&sDynamicReferencesLock);
 
        // flush find-by-address cache (do this after removed from master list, so there is no chance it can come back)
        if ( sLastImageByAddressCache == image )
@@ -1152,7 +1142,7 @@ static void checkDylibOverride(const char* dylibFile)
        //dyld::log("checkDylibOverride('%s')\n", dylibFile);
        uint32_t altVersion;
        char sysInstallName[PATH_MAX];
-       if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) ) {
+       if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) && (sysInstallName[0] =='/') ) {
                //dyld::log("%s has version 0x%08X and install name %s\n", dylibFile, altVersion, sysInstallName);
                uint32_t sysVersion;
                if ( getDylibVersionAndInstallname(sysInstallName, &sysVersion, NULL) ) {
@@ -1200,8 +1190,8 @@ static void checkDylibOverridesInDir(const char* dirPath)
 {
        //dyld::log("checkDylibOverridesInDir('%s')\n", dirPath);
        char dylibPath[PATH_MAX];
-       int dirPathLen = strlen(dirPath);
-       strlcpy(dylibPath, dirPath, PATH_MAX); 
+       if ( strlcpy(dylibPath, dirPath, PATH_MAX) >= PATH_MAX )
+               return;
        DIR* dirp = opendir(dirPath);
        if ( dirp != NULL) {
                dirent entry;
@@ -1211,9 +1201,9 @@ static void checkDylibOverridesInDir(const char* dirPath)
                                break;
                        if ( entp->d_type != DT_REG ) 
                                continue;
-                       dylibPath[dirPathLen] = '/';     
-                       dylibPath[dirPathLen+1] = '\0';     
-                       if ( strlcat(dylibPath, entp->d_name, PATH_MAX) > PATH_MAX ) 
+                       if ( strlcat(dylibPath, "/", PATH_MAX) >= PATH_MAX )
+                               continue;
+                       if ( strlcat(dylibPath, entp->d_name, PATH_MAX) >= PATH_MAX )
                                continue;
                        checkDylibOverride(dylibPath);
                }
@@ -1226,8 +1216,8 @@ static void checkFrameworkOverridesInDir(const char* dirPath)
 {
        //dyld::log("checkFrameworkOverridesInDir('%s')\n", dirPath);
        char frameworkPath[PATH_MAX];
-       int dirPathLen = strlen(dirPath);
-       strlcpy(frameworkPath, dirPath, PATH_MAX); 
+       if ( strlcpy(frameworkPath, dirPath, PATH_MAX) >= PATH_MAX )
+               return;
        DIR* dirp = opendir(dirPath);
        if ( dirp != NULL) {
                dirent entry;
@@ -1237,18 +1227,18 @@ static void checkFrameworkOverridesInDir(const char* dirPath)
                                break;
                        if ( entp->d_type != DT_DIR ) 
                                continue;
-                       frameworkPath[dirPathLen] = '/';     
-                       frameworkPath[dirPathLen+1] = '\0';
+                       if ( strlcat(frameworkPath, "/", PATH_MAX) >= PATH_MAX )
+                               continue;
                        int dirNameLen = strlen(entp->d_name);
                        if ( dirNameLen < 11 )
                                continue;
                        if ( strcmp(&entp->d_name[dirNameLen-10], ".framework") != 0 )
                                continue;
-                       if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) > PATH_MAX ) 
+                       if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) >= PATH_MAX )
                                continue;
-                       if ( strlcat(frameworkPath, "/", PATH_MAX) > PATH_MAX ) 
+                       if ( strlcat(frameworkPath, "/", PATH_MAX) >= PATH_MAX )
                                continue;
-                       if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) > PATH_MAX ) 
+                       if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) >= PATH_MAX )
                                continue;
                        frameworkPath[strlen(frameworkPath)-10] = '\0';
                        checkDylibOverride(frameworkPath);
@@ -1736,16 +1726,47 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
                strlcat(sLoadingCrashMessage, ", ignoring DYLD_* env vars", sizeof(sLoadingCrashMessage));
 }
 
+static void defaultUninitializedFallbackPaths(const char* envp[])
+{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment
+       const char* home = _simple_getenv(envp, "HOME");;
+       if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) {
+               const char** fpaths = sFrameworkFallbackPaths;
+               if ( home == NULL )
+                       removePathWithPrefix(fpaths, "$HOME");
+               else
+                       paths_expand_roots(fpaths, "$HOME", home);
+               sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = fpaths;
+       }
 
-static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron)
+    // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment
+       if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) {
+               const char** lpaths = sLibraryFallbackPaths;
+               if ( home == NULL )
+                       removePathWithPrefix(lpaths, "$HOME");
+               else
+                       paths_expand_roots(lpaths, "$HOME", home);
+               sEnv.DYLD_FALLBACK_LIBRARY_PATH = lpaths;
+       }
+#else
+       if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL )
+               sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sFrameworkFallbackPaths;
+
+       if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL )
+               sEnv.DYLD_FALLBACK_LIBRARY_PATH = sLibraryFallbackPaths;
+#endif
+}
+
+
+static void checkEnvironmentVariables(const char* envp[])
 {
-       const char* home = NULL;
        const char** p;
        for(p = envp; *p != NULL; p++) {
                const char* keyEqualsValue = *p;
            if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) {
                        const char* equals = strchr(keyEqualsValue, '=');
-                       if ( (equals != NULL) && !ignoreEnviron ) {
+                       if ( equals != NULL ) {
                                strlcat(sLoadingCrashMessage, "\n", sizeof(sLoadingCrashMessage));
                                strlcat(sLoadingCrashMessage, keyEqualsValue, sizeof(sLoadingCrashMessage));
                                const char* value = &equals[1];
@@ -1756,9 +1777,6 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron)
                                processDyldEnvironmentVariable(key, value, NULL);
                        }
                }
-           else if ( strncmp(keyEqualsValue, "HOME=", 5) == 0 ) {
-                       home = &keyEqualsValue[5];
-               }
                else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) {
                        const char* path = &keyEqualsValue[16];
                        sEnv.LD_LIBRARY_PATH = parseColonList(path, NULL);
@@ -1769,39 +1787,43 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron)
        checkLoadCommandEnvironmentVariables();
 #endif // SUPPORT_LC_DYLD_ENVIRONMENT  
        
-       // 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 )
-                       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 ) 
-                       removePathWithPrefix(paths, "$HOME");
-               else
-                       paths_expand_roots(paths, "$HOME", home);
-               sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths;
-       }
-       
        // <rdar://problem/11281064> DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together
        if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) {
                dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n");
                gLinkContext.imageSuffix = NULL;
        }
-       
-#if SUPPORT_VERSIONED_PATHS
-       checkVersionedPaths();
-#endif 
 }
 
-
-static void getHostInfo()
+#if __x86_64__
+static bool isGCProgram(const macho_header* mh, uintptr_t slide)
+{
+       const uint32_t cmd_count = mh->ncmds;
+       const struct load_command* const cmds = (struct load_command*)(((char*)mh)+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;
+                               if (strcmp(seg->segname, "__DATA") == 0) {
+                                       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) {
+                                               if (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) {
+                                                       const uint32_t*  objcInfo = (uint32_t*)(sect->addr + slide);
+                                                       return (objcInfo[1] & 6); // 6 = (OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC)
+                                               }
+                                       }
+                               }
+                       }
+                       break;
+               }
+               cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+       }
+       return false;
+}
+#endif
+static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide)
 {
 #if CPU_SUBTYPES_SUPPORTED
 #if __ARM_ARCH_7K__
@@ -1829,6 +1851,22 @@ static void getHostInfo()
        sHostCPU                = info.cpu_type;
        sHostCPUsubtype = info.cpu_subtype;
        mach_port_deallocate(mach_task_self(), hostPort);
+  #if __x86_64__
+       #if TARGET_IPHONE_SIMULATOR
+         sHaswell = false;
+       #else
+         sHaswell = (sHostCPUsubtype == CPU_SUBTYPE_X86_64_H);
+         // <rdar://problem/18528074> x86_64h: Fall back to the x86_64 slice if an app requires GC.
+         if ( sHaswell ) {
+               if ( isGCProgram(mainExecutableMH, mainExecutableSlide) ) {
+                       // When running a GC program on a haswell machine, don't use and 'h slices
+                       sHostCPUsubtype = CPU_SUBTYPE_X86_64_ALL;
+                       sHaswell = false;
+                       gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
+               }
+         }
+       #endif
+  #endif
 #endif
 #endif
 }
@@ -2389,11 +2427,13 @@ static bool isSimulatorBinary(const uint8_t* firstPage, const char* path)
        for (uint32_t i = 0; i < cmd_count; ++i) {
                switch (cmd->cmd) {
                        case LC_VERSION_MIN_IPHONEOS:
+                       case LC_VERSION_MIN_TVOS:
+                       case LC_VERSION_MIN_WATCHOS:
                                return true;
                        case LC_VERSION_MIN_MACOSX:
                                // grandfather in a few libSystem dylibs
-                               if ( strncmp(path, "/usr/lib/system/libsystem_", 26) == 0 )
-                                       return true;
+                               if (strstr(path, "/usr/lib/system") || strstr(path, "/usr/lib/libSystem"))
+                                       return true;
                                return false;
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
@@ -2457,12 +2497,16 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char*
                        default:
                                throw "mach-o, but wrong filetype";
                }
-               
+
 #if TARGET_IPHONE_SIMULATOR    
+       #if TARGET_OS_WATCH || TARGET_OS_TV
+               // disable error during bring up of these simulators
+       #else
                // <rdar://problem/14168872> dyld_sim should restrict loading osx binaries
                if ( !isSimulatorBinary(firstPage, path) ) {
                        throw "mach-o, but not built for iOS simulator";
                }
+       #endif
 #endif
 
                // instantiate an image
@@ -2738,7 +2782,7 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load
        ImageLoader* image = NULL;
        if ( strncmp(path, "@executable_path/", 17) == 0 ) {
                // executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305
-               if ( sProcessIsRestricted 
+               if ( sProcessIsRestricted && !sProcessRequiresLibraryValidation )
                        throwf("unsafe use of @executable_path in %s with restricted binary", context.origin);
                // handle @executable_path path prefix
                const char* executablePath = sExecPath;
@@ -2770,7 +2814,7 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load
        }
        else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) {
                // @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305
-               if ( sProcessIsRestricted && (strcmp(context.origin, sExecPath) == 0) )
+               if ( sProcessIsRestricted && (strcmp(context.origin, sExecPath) == 0) && !sProcessRequiresLibraryValidation )
                        throwf("unsafe use of @loader_path in %s with restricted binary", context.origin);
                // handle @loader_path path prefix
                char newPath[strlen(context.origin) + strlen(path)];
@@ -2834,7 +2878,7 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load
                if ( (exceptions != NULL) && (trailingPath != path) )
                        return NULL;
        }
-       else if (sProcessIsRestricted && (path[0] != '/' )) {
+       else if (sProcessIsRestricted && (path[0] != '/' ) && !sProcessRequiresLibraryValidation) {
                throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin);
        }
        
@@ -3196,6 +3240,23 @@ const void*      imMemorySharedCacheHeader()
        return sSharedCache;
 }
 
+const char* getStandardSharedCacheFilePath()
+{
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+       return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME;
+#else
+  #if __x86_64__
+       if ( sHaswell ) {
+               const char* path2 = MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H;
+               struct stat statBuf;
+               if ( my_stat(path2, &statBuf) == 0 )
+                       return path2;
+       }
+  #endif
+       return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME;
+#endif
+}
+
 int openSharedCacheFile()
 {
        char path[MAXPATHLEN];
@@ -3563,7 +3624,8 @@ static void mapSharedCache()
                        // zero size in header means signature runs to end-of-file
                        if ( signatureSize == 0 ) {
                                struct stat stat_buf;
-                               if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 ) 
+                               // FIXME: need size of cache file actually used
+                               if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 )
                                        signatureSize = stat_buf.st_size - header->codeSignatureOffset;
                        }
                        if ( signatureSize != 0 ) {
@@ -4021,16 +4083,6 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha
 }
 
 
-#if __LP64__
-       #define LC_SEGMENT_COMMAND              LC_SEGMENT_64
-       #define macho_segment_command   segment_command_64
-       #define macho_section                   section_64
-#else
-       #define LC_SEGMENT_COMMAND              LC_SEGMENT
-       #define macho_segment_command   segment_command
-       #define macho_section                   section
-#endif
-
 
 //
 // Look for a special segment in the mach header. 
@@ -4066,20 +4118,18 @@ static bool hasRestrictedSegment(const macho_header* mh)
        return false;
 }
 
+
 #if SUPPORT_VERSIONED_PATHS
-//
-// Peeks at a dylib file and returns its current_version and install_name.
-// Returns false on error.
-//                     
-static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName)
+
+static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) 
 {
+       firstPage[0] = 0;
        // open file (automagically closed when this function exits)
        FileOpener file(dylibPath);
-       
+
        if ( file.getFileDescriptor() == -1 ) 
                return false;
        
-       uint8_t firstPage[4096];
        if ( pread(file.getFileDescriptor(), firstPage, 4096, 0) != 4096 )
                return false;
 
@@ -4096,9 +4146,33 @@ static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* versi
                        return false;
                }
        }
+       
+       return true;
+}
+
+//
+// Peeks at a dylib file and returns its current_version and install_name.
+// Returns false on error.
+//
+static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName)
+{
+       uint8_t firstPage[4096];
+       const macho_header* mh = (macho_header*)firstPage;
+       if ( !readFirstPage(dylibPath, firstPage) ) {
+       #if DYLD_SHARED_CACHE_SUPPORT
+               // If file cannot be read, check to see if path is in shared cache
+               const macho_header* mhInCache;
+               const char*                     pathInCache;
+               long                            slideInCache;
+               if ( !findInSharedCacheImage(dylibPath, true, NULL, &mhInCache, &pathInCache, &slideInCache) )
+                       return false;
+               mh = mhInCache;
+       #else
+               return false;
+       #endif
+       }
 
        // check mach-o header
-       const mach_header* mh = (mach_header*)firstPage;
        if ( mh->magic != sMainExecutableMachHeader->magic ) 
                return false;
        if ( mh->cputype != sMainExecutableMachHeader->cputype )
@@ -4212,13 +4286,16 @@ void garbageCollectImages()
                for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
                        ImageLoader* image = *it;
                        if ( (image->dlopenCount() != 0) || image->neverUnload() ) {
-                               image->markedUsedRecursive(sDynamicReferences);
+                               OSSpinLockLock(&sDynamicReferencesLock);
+                                       image->markedUsedRecursive(sDynamicReferences);
+                               OSSpinLockUnlock(&sDynamicReferencesLock);
                        }
                }
 
                // collect phase: build array of images not marked in-use
                ImageLoader* deadImages[sAllImages.size()];
                unsigned deadCount = 0;
+               int maxRangeCount = 0;
                unsigned i = 0;
                for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
                        ImageLoader* image = *it;
@@ -4226,11 +4303,11 @@ void garbageCollectImages()
                                deadImages[i++] = image;
                                if (gLogAPIs) dyld::log("dlclose(), found unused image %p %s\n", image, image->getShortName());
                                ++deadCount;
+                               maxRangeCount += image->segmentCount();
                        }
                }
 
                // collect phase: run termination routines for images not marked in-use
-               const int maxRangeCount = deadCount*2;
                __cxa_range_t ranges[maxRangeCount];
                int rangeCount = 0;
                for (unsigned i=0; i < deadCount; ++i) {
@@ -4309,25 +4386,6 @@ void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths)
        preflight_finally(image);
 }
 
-#if __x86_64__
-static bool isHaswell()
-{
-#if TARGET_IPHONE_SIMULATOR
-       return false;
-#else
-       // check system is capable of running x86_64h code
-       struct host_basic_info info;
-       mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
-       mach_port_t hostPort = mach_host_self();
-       kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
-       mach_port_deallocate(mach_task_self(), hostPort);
-       if ( result != KERN_SUCCESS )
-               return false;
-       return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H );
-#endif
-}
-#endif
-
 static void loadInsertedDylib(const char* path)
 {
        ImageLoader* image = NULL;
@@ -4358,24 +4416,34 @@ static void loadInsertedDylib(const char* path)
        }
 }
 
-static bool processRestricted(const macho_header* mainExecutableMH)
+static bool processRestricted(const macho_header* mainExecutableMH, bool* ignoreEnvVars, bool* processRequiresLibraryValidation)
 {      
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_IPHONE_SIMULATOR
+       gLinkContext.codeSigningEnforced = true;
+#else
     // ask kernel if code signature of program makes it restricted
     uint32_t flags;
        if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
+               if (flags & CS_REQUIRE_LV)
+                       *processRequiresLibraryValidation = true;
+
+  #if __MAC_OS_X_VERSION_MIN_REQUIRED
                if ( flags & CS_ENFORCEMENT ) {
                        gLinkContext.codeSigningEnforced = true;
                }
+               if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) ) {
+                       sRestrictedReason = restrictedByEntitlements;
+                       return true;
+               }
+  #else
+               if ((flags & CS_ENFORCEMENT) && !(flags & CS_GET_TASK_ALLOW)) {
+                       *ignoreEnvVars = true;
+               }
+               gLinkContext.codeSigningEnforced = true;
+  #endif
        }
-       if (flags & CS_RESTRICT) {
-               sRestrictedReason = restrictedByEntitlements;
-               return true;
-       }
-#else
-       gLinkContext.codeSigningEnforced = true;
 #endif
-       
+
        // all processes with setuid or setgid bit set are restricted
     if ( issetugid() ) {
                sRestrictedReason = restrictedBySetGUid;
@@ -4426,7 +4494,7 @@ typedef int (*fcntl_proc_t)(int, int, void*);
 typedef int (*ioctl_proc_t)(int, unsigned long, void*);
 static void* getProcessInfo() { return dyld::gProcessInfo; }
 static SyscallHelpers sSysCalls = {
-               3,
+               4,
                // added in version 1
                (open_proc_t)&open, 
                &close, 
@@ -4462,7 +4530,10 @@ static SyscallHelpers sSysCalls = {
                // added in version 3
                &opendir,
                &readdir_r,
-               &closedir
+               &closedir,
+               // added in version 4
+               &coresymbolication_load_notifier,
+               &coresymbolication_unload_notifier
 };
 
 __attribute__((noinline))
@@ -4498,32 +4569,73 @@ static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH,
        
        // calculate total size of dyld segments
        const macho_header* mh = (const macho_header*)firstPage;
+       struct macho_segment_command* lastSeg = NULL;
+       struct macho_segment_command* firstSeg = NULL;
        uintptr_t mappingSize = 0;
        uintptr_t preferredLoadAddress = 0;
        const uint32_t cmd_count = mh->ncmds;
+       if ( (sizeof(macho_header) + mh->sizeofcmds) > 4096 )
+               return 0;
        const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
+       const struct load_command* const endCmds = (struct load_command*)(((char*)mh) + sizeof(macho_header) + mh->sizeofcmds);
        const struct load_command* cmd = cmds;
        for (uint32_t i = 0; i < cmd_count; ++i) {
+               uint32_t cmdLength = cmd->cmdsize;
+               if ( cmdLength < 8 )
+                       return 0;
+               const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength);
+               if ( (nextCmd > endCmds) || (nextCmd < cmd) )
+                       return 0;
                switch (cmd->cmd) {
                        case LC_SEGMENT_COMMAND:
                                {
                                        struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
-                                       mappingSize += seg->vmsize;
-                                       if ( seg->fileoff == 0 )
+                                       if ( seg->vmaddr + seg->vmsize < seg->vmaddr )
+                                               return 0;
+                                       if ( seg->vmsize < seg->filesize )
+                                               return 0;
+                                       if ( lastSeg == NULL ) {
+                                               // first segment must be __TEXT and start at beginning of file/slice
+                                               firstSeg = seg;
+                                               if ( strcmp(seg->segname, "__TEXT") != 0 )
+                                                       return 0;
+                                               if ( seg->fileoff != 0 )
+                                                       return 0;
+                                               if ( seg->filesize < (sizeof(macho_header) + mh->sizeofcmds) )
+                                                       return 0;
                                                preferredLoadAddress = seg->vmaddr;
+                                       }
+                                       else {
+                                               // other sements must be continguous with previous segment and not executable
+                                               if ( lastSeg->fileoff + lastSeg->filesize != seg->fileoff )
+                                                       return 0;
+                                               if ( lastSeg->vmaddr + lastSeg->vmsize != seg->vmaddr )
+                                                       return 0;
+                                               if ( (seg->initprot & VM_PROT_EXECUTE) != 0 )
+                                                       return 0;
+                                       }
+                                       mappingSize += seg->vmsize;
+                                       lastSeg = seg;
                                }
                                break;
+                       case LC_SEGMENT_COMMAND_WRONG:
+                               return 0;
                }
-               cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+               cmd = nextCmd;
        }
+       // last segment must be named __LINKEDIT and not writable
+       if ( strcmp(lastSeg->segname, "__LINKEDIT") != 0 )
+               return 0;
+       if ( lastSeg->initprot & VM_PROT_WRITE )
+               return 0;
 
        // reserve space, then mmap each segment
        vm_address_t loadAddress = 0;
-       uintptr_t entry = 0;
        if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 )
                return 0;
        cmd = cmds;
        struct linkedit_data_command* codeSigCmd = NULL;
+       struct source_version_command* dyldVersionCmd = NULL;
        for (uint32_t i = 0; i < cmd_count; ++i) {
                switch (cmd->cmd) {
                        case LC_SEGMENT_COMMAND:
@@ -4536,26 +4648,23 @@ static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH,
                                                return 0;
                                }
                                break;
-                       case LC_UNIXTHREAD:
-                               {
-                               #if __i386__
-                                       const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
-                                       entry = (registers->__eip + loadAddress - preferredLoadAddress);
-                               #elif __x86_64__
-                                       const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
-                                       entry = (registers->__rip + loadAddress - preferredLoadAddress);
-                               #endif
-                               }
-                               break;
                        case LC_CODE_SIGNATURE:
                                codeSigCmd = (struct linkedit_data_command*)cmd;
                                break;
+                       case LC_SOURCE_VERSION:
+                               dyldVersionCmd = (struct source_version_command*)cmd;
+                               break;
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
-       
+
+       // must have code signature which is contained within LINKEDIT segment
        if ( codeSigCmd == NULL )
                return 0;
+       if ( codeSigCmd->dataoff < lastSeg->fileoff )
+               return 0;
+       if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) )
+               return 0;
 
        fsignatures_t siginfo;
        siginfo.fs_file_start=fileOffset;                                                       // start of mach-o slice in fat file 
@@ -4566,8 +4675,37 @@ static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH,
                dyld::log("fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d\n", errno);
                return 0;
        }
-
        close(fd);
+       // file range covered by code signature must extend up to code signature itself
+       if ( siginfo.fs_file_start < codeSigCmd->dataoff )
+               return 0;
+
+       // walk newly mapped dyld_sim __TEXT load commands to find entry point
+       uintptr_t entry = 0;
+       cmd = (struct load_command*)(((char*)loadAddress)+sizeof(macho_header));
+       const uint32_t count = ((macho_header*)(loadAddress))->ncmds;
+       for (uint32_t i = 0; i < count; ++i) {
+               if (cmd->cmd == LC_UNIXTHREAD) {
+               #if __i386__
+                       const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
+                       // entry point must be in first segment
+                       if ( registers->__eip < firstSeg->vmaddr )
+                               return 0;
+                       if ( registers->__eip > (firstSeg->vmaddr + firstSeg->vmsize) )
+                               return 0;
+                       entry = (registers->__eip + loadAddress - preferredLoadAddress);
+               #elif __x86_64__
+                       const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
+                       // entry point must be in first segment
+                       if ( registers->__rip < firstSeg->vmaddr )
+                               return 0;
+                       if ( registers->__rip > (firstSeg->vmaddr + firstSeg->vmsize) )
+                               return 0;
+                       entry = (registers->__rip + loadAddress - preferredLoadAddress);
+               #endif
+               }
+               cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+       }
 
        // notify debugger that dyld_sim is loaded
        dyld_image_info info;
@@ -4576,13 +4714,14 @@ static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH,
        info.imageFileModDate = sb.st_mtime;
        addImagesToAllImages(1, &info);
        dyld::gProcessInfo->notification(dyld_image_adding, 1, &info);
-       
+
+       const char** appleParams = apple;
        // jump into new simulator dyld
        typedef uintptr_t (*sim_entry_proc_t)(int argc, const char* argv[], const char* envp[], const char* apple[],
                                                                const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide,
                                                                const dyld::SyscallHelpers* vtable, uintptr_t* startGlue);
        sim_entry_proc_t newDyld = (sim_entry_proc_t)entry;
-       return (*newDyld)(argc, argv, envp, apple, mainExecutableMH, (macho_header*)loadAddress, 
+       return (*newDyld)(argc, argv, envp, appleParams, mainExecutableMH, (macho_header*)loadAddress,
                                         loadAddress - preferredLoadAddress, 
                                         &sSysCalls, startGlue);
 }
@@ -4649,7 +4788,6 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        // <rdar://problem/13868260> Remove interim apple[0] transition code from dyld
        if (!sExecPath) sExecPath = apple[0];
        
-       sExecPath = apple[0];
        bool ignoreEnvironmentVariables = false;
        if ( sExecPath[0] != '/' ) {
                // have relative path, use cwd to make absolute
@@ -4669,25 +4807,25 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                ++sExecShortName;
        else
                sExecShortName = sExecPath;
-    sProcessIsRestricted = processRestricted(mainExecutableMH);
+    sProcessIsRestricted = processRestricted(mainExecutableMH, &ignoreEnvironmentVariables, &sProcessRequiresLibraryValidation);
     if ( sProcessIsRestricted ) {
 #if SUPPORT_LC_DYLD_ENVIRONMENT
                checkLoadCommandEnvironmentVariables();
-#if SUPPORT_VERSIONED_PATHS
-               checkVersionedPaths();
-#endif 
 #endif         
                pruneEnvironmentVariables(envp, &apple);
                // set again because envp and apple may have changed or moved
                setContext(mainExecutableMH, argc, argv, envp, apple);
        }
-       else
-               checkEnvironmentVariables(envp, ignoreEnvironmentVariables);
-       if ( sEnv.DYLD_PRINT_OPTS ) 
+       else {
+               if ( !ignoreEnvironmentVariables )
+                       checkEnvironmentVariables(envp);
+               defaultUninitializedFallbackPaths(envp);
+       }
+       if ( sEnv.DYLD_PRINT_OPTS )
                printOptions(argv);
        if ( sEnv.DYLD_PRINT_ENV ) 
                printEnvironmentVariables(envp);
-       getHostInfo();
+       getHostInfo(mainExecutableMH, mainExecutableSlide);
        // install gdb notifier
        stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
        stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
@@ -4713,9 +4851,13 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
                gLinkContext.mainExecutable = sMainExecutable;
                gLinkContext.processIsRestricted = sProcessIsRestricted;
+               gLinkContext.processRequiresLibraryValidation = sProcessRequiresLibraryValidation;
                gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
 
 #if TARGET_IPHONE_SIMULATOR
+       #if TARGET_OS_WATCH || TARGET_OS_TV
+               // disable error during bring up of these simulators
+       #else
                // check main executable is not too new for this OS
                {
                        if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) {
@@ -4731,17 +4873,21 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                                                dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF));
                        }
                }
+       #endif
 #endif
 
                // load shared cache
-       #if __x86_64__
-               sHaswell = isHaswell();
-       #endif
                checkSharedRegionDisable();
        #if DYLD_SHARED_CACHE_SUPPORT
                if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion )
                        mapSharedCache();
        #endif
+
+               // Now that shared cache is loaded, setup an versioned dylib overrides
+       #if SUPPORT_VERSIONED_PATHS
+               checkVersionedPaths();
+       #endif
+
                // load any inserted libraries
                if      ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
                        for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) 
@@ -4776,6 +4922,15 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                                image->registerInterposing();
                        }
                }
+
+               // <rdar://problem/19315404> dyld should support interposition even without DYLD_INSERT_LIBRARIES
+               for (int i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) {
+                       ImageLoader* image = sAllImages[i];
+                       if ( image->inSharedCache() )
+                               continue;
+                       image->registerInterposing();
+               }
+
                // apply interposing to initial set of images
                for(int i=0; i < sImageRoots.size(); ++i) {
                        sImageRoots[i]->applyInterposing(gLinkContext);
index 3cdc3c7fc998df2ea299e08a154e6860e67ea486..fe6c9b687281269fd7db440ecf63b7cb655c20b1 100644 (file)
@@ -114,6 +114,7 @@ namespace dyld {
        extern void                                     logBindings(const char* format, ...);
 #endif
        extern bool                                     processIsRestricted();
+       extern const char*                      getStandardSharedCacheFilePath();
        extern int                                      my_stat(const char* path, struct stat* buf);
        extern int                                      my_open(const char* path, int flag, int other);
 }
index 2c888ced856fa8655fae3e5aeefcd51731a4e28a..b64331a3e4d04c3b620b51053988b78b8410717e 100644 (file)
@@ -147,10 +147,13 @@ static struct dyld_func dyld_funcs[] = {
        {"__dyld_shared_cache_some_image_overridden",           (void*)dyld_shared_cache_some_image_overridden },
        {"__dyld_process_is_restricted",                                        (void*)dyld::processIsRestricted },
        {"__dyld_dynamic_interpose",                                            (void*)dyld_dynamic_interpose },
+#if DYLD_SHARED_CACHE_SUPPORT
+       {"__dyld_shared_cache_file_path",                                       (void*)dyld::getStandardSharedCacheFilePath },
+#endif
+    {"__dyld_get_image_header_containing_address",             (void*)dyld_image_header_containing_address },
 
        // deprecated
 #if DEPRECATED_APIS_SUPPORTED
-    {"__dyld_get_image_header_containing_address",             (void*)_dyld_get_image_header_containing_address },
     {"__dyld_lookup_and_bind",                                         (void*)client_dyld_lookup_and_bind },
     {"__dyld_lookup_and_bind_with_hint",                       (void*)_dyld_lookup_and_bind_with_hint },
     {"__dyld_lookup_and_bind_fully",                           (void*)_dyld_lookup_and_bind_fully },
@@ -341,7 +344,7 @@ const char* _dyld_get_image_name(uint32_t image_index)
                return NULL;
 }
 
-const struct mach_header * _dyld_get_image_header_containing_address(const void* address)
+const struct mach_header * dyld_image_header_containing_address(const void* address)
 {
        if ( dyld::gLogAPIs )
                dyld::log("%s(%p)\n", __func__, address);
@@ -1115,7 +1118,7 @@ bool NSUnLinkModule(NSModule module, uint32_t options)
                return false;
        dyld::runImageStaticTerminators(image);
        if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 13) ) {
-               __cxa_range_t ranges[3];
+               __cxa_range_t ranges[image->segmentCount()];
                int rangeCount = 0;
                for (unsigned int j=0; j < image->segmentCount(); ++j) {
                        if ( !image->segExecutable(j) )
@@ -1417,15 +1420,16 @@ void* dlopen(const char* path, int mode)
        void* result = NULL;
        ImageLoader* image = NULL;
        std::vector<const char*> rpathsFromCallerImage;
+       ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage);
        try {
                void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
                ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
-               // for dlopen, use rpath from caller image and from main executable
-               if ( callerImage != NULL )
-                       callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage);
-               ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage);
-               if ( callerImage != dyld::mainExecutable() ) {
-                       dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage);
+               if ( (mode & RTLD_NOLOAD) == 0 ) {
+                       // for dlopen, use rpath from caller image and from main executable
+                       if ( callerImage != NULL )
+                               callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage);
+                       if ( callerImage != dyld::mainExecutable() )
+                               dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage);
                }
  
                const bool leafName = (strchr(path, '/') == NULL);
index 6ded1a7e7f430ccc6297bdeb535159544c1c0f1a..d1e48f51f6461974ea8c9f6cb26c2fa70aeb9925 100644 (file)
@@ -58,6 +58,14 @@ extern "C" void __cxa_finalize_ranges(const struct __cxa_range_t ranges[], int c
        #define LC_VERSION_MIN_IPHONEOS 0x25
 #endif
 
+#ifndef LC_VERSION_MIN_TVOS
+       #define LC_VERSION_MIN_TVOS 0x2F
+#endif
+
+#ifndef LC_VERSION_MIN_WATCHOS
+       #define LC_VERSION_MIN_WATCHOS 0x30
+#endif
+
 
 #ifndef LC_LOAD_UPWARD_DYLIB
        #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
@@ -412,19 +420,46 @@ int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
        return (-1);
 }
 
+
 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
 
 
-/*
- * Returns the sdk version (encode as nibble XXXX.YY.ZZ) the
- * specified binary was built against.
- *
- * First looks for LC_VERSION_MIN_MACOSX/LC_VERSION_MIN_IPHONEOS
- * in binary and if sdk field is not zero, return that value.
- * Otherwise, looks for the libSystem.B.dylib the binary linked
- * against and uses a table to convert that to an sdk version.
- */
-uint32_t dyld_get_sdk_version(const mach_header* mh)
+static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* loadCommand, uint32_t* minOS, uint32_t* sdk)
+{
+       const load_command* startCmds = NULL;
+       if ( mh->magic == MH_MAGIC_64 )
+               startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
+       else if ( mh->magic == MH_MAGIC )
+               startCmds = (load_command*)((char *)mh + sizeof(mach_header));
+       else
+               return false;  // not a mach-o file, or wrong endianness
+               
+       const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
+       const load_command* cmd = startCmds;
+       for(uint32_t i = 0; i < mh->ncmds; ++i) {
+           const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
+               if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) {
+                       return 0;
+               }
+               const version_min_command* versCmd;
+               switch ( cmd->cmd ) {
+                       case LC_VERSION_MIN_IPHONEOS:
+                       case LC_VERSION_MIN_MACOSX:
+                       case LC_VERSION_MIN_TVOS:
+                       case LC_VERSION_MIN_WATCHOS:
+                               versCmd = (version_min_command*)cmd;
+                               *loadCommand = versCmd->cmd;
+                               *minOS = versCmd->version;
+                               *sdk = versCmd->sdk;
+                               return true;
+               }
+               cmd = nextCmd;
+       }
+       return false;
+}
+
+#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED
+static uint32_t deriveSDKVersFromDylibs(const mach_header* mh)
 {
        const load_command* startCmds = NULL;
        if ( mh->magic == MH_MAGIC_64 )
@@ -435,15 +470,14 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                return 0;  // not a mach-o file, or wrong endianness
                
        const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
-       const version_min_command* versCmd;
        const dylib_command* dylibCmd;
        const load_command* cmd = startCmds;
        const char* dylibName;
-#if __IPHONE_OS_VERSION_MIN_REQUIRED 
+  #if __IPHONE_OS_VERSION_MIN_REQUIRED
        uint32_t foundationVers = 0;
-#else
+  #else
        uint32_t libSystemVers = 0;
-#endif
+  #endif
        for(uint32_t i = 0; i < mh->ncmds; ++i) {
            const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
                // <rdar://problem/14381579&16050962> sanity check size of command
@@ -451,20 +485,6 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                        return 0;
                }
                switch ( cmd->cmd ) {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED 
-                       case LC_VERSION_MIN_IPHONEOS:
-#else
-                       case LC_VERSION_MIN_MACOSX:
-#endif
-                               versCmd = (version_min_command*)cmd;
-#ifdef DICE_KIND_DATA
-                               if ( versCmd->sdk != 0 )
-                                       return versCmd->sdk;    // found explicit SDK version
-#else
-                               if ( versCmd->reserved != 0 )
-                                       return versCmd->reserved;       // found explicit SDK version
-#endif
-                               break;
                        case LC_LOAD_DYLIB:
                        case LC_LOAD_WEAK_DYLIB:
                        case LC_LOAD_UPWARD_DYLIB:
@@ -473,13 +493,13 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                                if ( dylibCmd->dylib.name.offset > cmd->cmdsize )
                                        return 0;
                                dylibName = (char*)dylibCmd + dylibCmd->dylib.name.offset;
-#if __IPHONE_OS_VERSION_MIN_REQUIRED          
+  #if __IPHONE_OS_VERSION_MIN_REQUIRED
                                if ( strcmp(dylibName, "/System/Library/Frameworks/Foundation.framework/Foundation") == 0 )
                                        foundationVers = dylibCmd->dylib.current_version;
-#else
+  #else
                                if ( strcmp(dylibName, "/usr/lib/libSystem.B.dylib") == 0 )
                                        libSystemVers = dylibCmd->dylib.current_version;
-#endif
+  #endif
                                break;
                }
                cmd = nextCmd;
@@ -490,7 +510,7 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                uint32_t osVersion;
        };
        
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+  #if __IPHONE_OS_VERSION_MIN_REQUIRED
        static const DylibToOSMapping foundationMapping[] = {
                { PACKED_VERSION(678,24,0), DYLD_IOS_VERSION_2_0 },
                { PACKED_VERSION(678,26,0), DYLD_IOS_VERSION_2_1 },
@@ -506,8 +526,10 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                { PACKED_VERSION(890,1,0),  DYLD_IOS_VERSION_5_1 },
                { PACKED_VERSION(992,0,0),  DYLD_IOS_VERSION_6_0 },
                { PACKED_VERSION(993,0,0),  DYLD_IOS_VERSION_6_1 },  
-               { PACKED_VERSION(1038,14,0),DYLD_IOS_VERSION_7_0 }, // check final
-               { PACKED_VERSION(0,0,0),    DYLD_IOS_VERSION_7_0 } 
+               { PACKED_VERSION(1038,14,0),DYLD_IOS_VERSION_7_0 },
+               { PACKED_VERSION(0,0,0),    DYLD_IOS_VERSION_7_0 }
+               // We don't need to expand this table because all recent
+               // binaries have LC_VERSION_MIN_ load command.
        };
 
        if ( foundationVers != 0 ) {
@@ -521,7 +543,7 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                }
        }
 
-#else
+  #else
        // Note: versions are for the GM release.  The last entry should
        // always be zero.  At the start of the next major version,
        // a new last entry needs to be added and the previous zero
@@ -534,6 +556,8 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                { PACKED_VERSION(169,3,0),  DYLD_MACOSX_VERSION_10_8 },
                { PACKED_VERSION(1197,0,0), DYLD_MACOSX_VERSION_10_9 },
                { PACKED_VERSION(0,0,0),    DYLD_MACOSX_VERSION_10_9 }
+               // We don't need to expand this table because all recent
+               // binaries have LC_VERSION_MIN_ load command.
        };
 
        if ( libSystemVers != 0 ) {
@@ -546,9 +570,82 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
                        lastOsVersion = p->osVersion;
                }
        }
+  #endif
+  return 0;
+}
 #endif
-       
+
+
+#if __WATCH_OS_VERSION_MIN_REQUIRED
+static uint32_t watchVersToIOSVers(uint32_t vers)
+{
+       return vers + 0x00070000;
+}
+
+uint32_t dyld_get_program_sdk_watch_os_version()
+{
+       const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
+       uint32_t loadCommand;
+       uint32_t minOS;
+       uint32_t sdk;
+
+       if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
+               if ( loadCommand == LC_VERSION_MIN_WATCHOS )
+                               return sdk;
+       }
+       return 0;
+}
+#endif
+
+/*
+ * Returns the sdk version (encode as nibble XXXX.YY.ZZ) the
+ * specified binary was built against.
+ *
+ * First looks for LC_VERSION_MIN_* in binary and if sdk field is 
+ * not zero, return that value.
+ * Otherwise, looks for the libSystem.B.dylib the binary linked
+ * against and uses a table to convert that to an sdk version.
+ */
+uint32_t dyld_get_sdk_version(const mach_header* mh)
+{
+       uint32_t loadCommand;
+       uint32_t minOS;
+       uint32_t sdk;
+
+       if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
+               switch (loadCommand) {
+#if __WATCH_OS_VERSION_MIN_REQUIRED
+                       case LC_VERSION_MIN_WATCHOS:
+                               // new binary. sdk version looks like "2.0" but API wants "9.0"
+                               return watchVersToIOSVers(sdk);
+                       case LC_VERSION_MIN_IPHONEOS:
+                               // old binary. sdk matches API semantics so can return directly.
+                               return sdk;
+#elif __TV_OS_VERSION_MIN_REQUIRED
+                       case LC_VERSION_MIN_TVOS:
+                       case LC_VERSION_MIN_IPHONEOS:
+                               return sdk;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+                       case LC_VERSION_MIN_IPHONEOS:
+                               if ( sdk != 0 ) // old binaries might not have SDK set
+                                       return sdk;
+                               break;
+#else
+                       case LC_VERSION_MIN_MACOSX:
+                               if ( sdk != 0 ) // old binaries might not have SDK set
+                                       return sdk;
+                               break;
+#endif
+               }
+       }
+
+#if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED
+       // All WatchOS and tv OS binaries should have version load command.
        return 0;
+#else
+       // MacOSX and iOS have old binaries without version load commmand.
+       return deriveSDKVersFromDylibs(mh);
+#endif
 }
 
 uint32_t dyld_get_program_sdk_version()
@@ -558,34 +655,31 @@ uint32_t dyld_get_program_sdk_version()
 
 uint32_t dyld_get_min_os_version(const struct mach_header* mh)
 {
-       const load_command* startCmds = NULL;
-       if ( mh->magic == MH_MAGIC_64 )
-               startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
-       else if ( mh->magic == MH_MAGIC )
-               startCmds = (load_command*)((char *)mh + sizeof(mach_header));
-       else
-               return 0;  // not a mach-o file, or wrong endianness
-               
-       const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
-       const version_min_command* versCmd;
-       const load_command* cmd = startCmds;
-       for(uint32_t i = 0; i < mh->ncmds; ++i) {
-           const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
-               // <rdar://problem/14381579&16050962> sanity check size of command
-               if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) {
-                       return 0;
-               }
-               switch ( cmd->cmd ) {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED          
+       uint32_t loadCommand;
+       uint32_t minOS;
+       uint32_t sdk;
+
+       if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) {
+               switch (loadCommand) {
+#if __WATCH_OS_VERSION_MIN_REQUIRED
+                       case LC_VERSION_MIN_WATCHOS:
+                               // new binary. OS version looks like "2.0" but API wants "9.0"
+                               return watchVersToIOSVers(minOS);
+                       case LC_VERSION_MIN_IPHONEOS:
+                               // old binary. OS matches API semantics so can return directly.
+                               return minOS;
+#elif __TV_OS_VERSION_MIN_REQUIRED
+                       case LC_VERSION_MIN_TVOS:
+                       case LC_VERSION_MIN_IPHONEOS:
+                               return minOS;
+#elif __IPHONE_OS_VERSION_MIN_REQUIRED
                        case LC_VERSION_MIN_IPHONEOS:
+                               return minOS;
 #else
                        case LC_VERSION_MIN_MACOSX:
+                               return minOS;
 #endif
-                               versCmd = (version_min_command*)cmd;
-                               return versCmd->version;        // found explicit min OS version
-                               break;
                }
-               cmd = nextCmd;
        }
        return 0;
 }
@@ -1448,6 +1542,17 @@ const char* dyld_image_path_containing_address(const void* addr)
        return p(addr);
 }
 
+const struct mach_header* dyld_image_header_containing_address(const void* addr)
+{
+       DYLD_NO_LOCK_THIS_BLOCK;
+    static const mach_header* (*p)(const void*) = NULL;
+
+       if(p == NULL)
+           _dyld_func_lookup("__dyld_get_image_header_containing_address", (void**)&p);
+       return p(addr);
+}
+
+
 bool dyld_shared_cache_some_image_overridden()
 {
        DYLD_NO_LOCK_THIS_BLOCK;
@@ -1469,6 +1574,17 @@ bool dyld_process_is_restricted()
        return p();
 }
 
+#if DYLD_SHARED_CACHE_SUPPORT
+const char* dyld_shared_cache_file_path()
+{
+       DYLD_NO_LOCK_THIS_BLOCK;
+    static const char* (*p)() = NULL;
+       
+       if(p == NULL)
+           _dyld_func_lookup("__dyld_shared_cache_file_path", (void**)&p);
+       return p();
+}
+#endif
 
 void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count)
 {
index d68fc4e01479dc6b7667892f84ecaacc304ea200..ebccc7778df008744dd423fef8ec1dddc4267dfc 100644 (file)
@@ -68,6 +68,10 @@ void* malloc(size_t size)
                return p;
        }
        else {
+               if ( size > DYLD_POOL_CHUNK_SIZE ) {
+                       dyld::log("dyld malloc overflow: size=%zu\n", size);
+                       exit(1);
+               }
                size = (size+sizeof(void*)-1) & (-sizeof(void*)); // pointer align
                uint8_t* result = currentPool->current;
                currentPool->current += size;
@@ -128,7 +132,13 @@ void* calloc(size_t count, size_t size)
                return result;
        }
        else {
-               return malloc(count*size);
+               // Check for overflow of integer multiplication
+               size_t total = count * size;
+               if ( total/count != size ) {
+                       dyld::log("dyld calloc overflow: count=%zu, size=%zu\n", count, size);
+                       exit(1);
+               }
+               return malloc(total);
        }
 }
 
index 58055cbb1b83f386f1f027037d98aee995ce8d19..71d45a3ccad603e8f3546e4790b620a23f7d85ff 100644 (file)
@@ -217,7 +217,7 @@ ___dso_handle:      .long 0
 __dyld_start:
        mov     r8, sp          // save stack pointer
        sub     sp, #16         // make room for outgoing parameters
-       bic     sp, sp, #7      // force 8-byte alignment
+       bic     sp, sp, #15     // force 16-byte alignment
 
        // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
 
index 12be7db67af6d708ff685f47c13e57ace782ba55..01a8e8028c7d7f7663231c864684895858719db1 100644 (file)
@@ -27,6 +27,7 @@
 #define __DYLD_SYSCALL_HELPERS__
 
 #include <dirent.h>
+#include <mach-o/loader.h>
 
 #if __cplusplus
 namespace dyld {
@@ -74,6 +75,9 @@ namespace dyld {
                DIR*                    (*opendir)(const char* path);
                int                     (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result);
                int                     (*closedir)(DIR* dirp);
+               // Added in version 4
+               void                    (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header);
+               void                    (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
        };
 
        extern const struct SyscallHelpers* gSyscallHelpers;
index c4c45efef423c0d8dd7efccfedc8ab2eda871ba0..5cc5658d2e64174a8c90781ded14d9d5b7c74cf1 100644 (file)
@@ -270,12 +270,14 @@ dyld_stub_binder:
 
 #if __ARM_ARCH_7K__
        vpush   {d0, d1, d2, d3, d4, d5, d6, d7}
+       sub     sp, sp, #8                      // Align stack to 16 bytes.
 #endif
        // call dyld::fastBindLazySymbol(loadercache, lazyinfo)
        bl      __Z21_dyld_fast_stub_entryPvl
        mov     ip, r0                          // move the symbol`s address into ip
 
 #if __ARM_ARCH_7K__
+       add     sp, sp, #8
        vpop    {d0, d1, d2, d3, d4, d5, d6, d7}
 #endif
 
index 5616c87d709b3f418fb27468636877a11d0f0ad1..bc7741f3ec2b864f0063e7d22e6180cff12578fa 100644 (file)
@@ -46,6 +46,7 @@
        #include "dyld_images.h"
        #include <mach-o/loader.h>
        #include <mach-o/nlist.h>
+       #include <mach/kern_return.h>
        #if __LP64__
                #define LC_SEGMENT_COMMAND                      LC_SEGMENT_64
                typedef struct segment_command_64       macho_segment_command;
@@ -339,6 +340,9 @@ int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other)
 //
 
 #if TARGET_IPHONE_SIMULATOR
+
+#include <coreSymbolicationDyldSupport.h>
+
 int myopen(const char* path, int oflag, int extra) __asm("_open");
 int myopen(const char* path, int oflag, int extra) {
        return gSyscallHelpers->open(path, oflag, extra);
@@ -463,6 +467,13 @@ uint64_t mach_absolute_time(void) {
        return gSyscallHelpers->mach_absolute_time();
 } 
 
+kern_return_t thread_switch(mach_port_name_t thread_name,
+                                                       int option, mach_msg_timeout_t option_time) {
+       if ( gSyscallHelpers->version < 2 )
+               return KERN_FAILURE;
+       return gSyscallHelpers->thread_switch(thread_name, option, option_time);
+}
+
 DIR* opendir(const char* path) {
        if ( gSyscallHelpers->version < 3 )
                return NULL;
@@ -481,15 +492,61 @@ int closedir(DIR* dirp) {
        return gSyscallHelpers->closedir(dirp);
 }
 
+#define SUPPORT_HOST_10_10  1
+
+#if SUPPORT_HOST_10_10
+typedef int               (*FuncPtr_nanosleep)(const struct timespec*, struct timespec*);
+typedef kern_return_t     (*FuncPtr_mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
+typedef mach_msg_return_t (*FuncPtr_mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
+typedef int               (*FuncPtr_kill)(pid_t pid, int sig);
+typedef pid_t             (*FuncPtr_getpid)();
+typedef bool              (*FuncPtr_OSAtomicCompareAndSwap32)(int32_t, int32_t, volatile int32_t*);
 
-typedef void (*LoadFuncPtr)(void* shm, void* image, uint64_t timestamp);
-typedef void (*UnloadFuncPtr)(void* shm, void* image);
+static FuncPtr_nanosleep                 proc_nanosleep = NULL;
+static FuncPtr_mach_port_allocate        proc_mach_port_allocate = NULL;
+static FuncPtr_mach_msg                  proc_mach_msg = NULL;
+static FuncPtr_kill                      proc_kill = NULL;
+static FuncPtr_getpid                    proc_getpid = NULL;
+static FuncPtr_OSAtomicCompareAndSwap32  proc_OSAtomicCompareAndSwap32 = NULL;
+
+
+int nanosleep(const struct timespec* p1, struct timespec* p2)
+{
+       return (*proc_nanosleep)(p1, p2);
+}
 
-static LoadFuncPtr   sLoadPtr = NULL;
-static UnloadFuncPtr sUnloadPtr = NULL;
+kern_return_t mach_port_allocate(ipc_space_t p1, mach_port_right_t p2, mach_port_name_t* p3)
+{
+       return (*proc_mach_port_allocate)(p1, p2, p3);
+}
+
+mach_msg_return_t mach_msg(mach_msg_header_t* p1, mach_msg_option_t p2, mach_msg_size_t p3, mach_msg_size_t p4, mach_port_name_t p5, mach_msg_timeout_t p6, mach_port_name_t p7)
+{
+       return (*proc_mach_msg)(p1, p2, p3, p4, p5, p6, p7);
+}
+
+int kill(pid_t p1, int p2)
+{
+       return (*proc_kill)(p1, p2);
+}
+
+pid_t getpid()
+{
+       return (*proc_getpid)();
+}
+
+bool OSAtomicCompareAndSwap32(int32_t p1, int32_t p2, volatile int32_t* p3)
+{
+       return (*proc_OSAtomicCompareAndSwap32)(p1, p2, p3);
+}
+
+
+// Look up sycalls in host dyld needed by coresymbolication_ routines in dyld_sim
+static void findHostFunctions() {
+       // Only look up symbols once
+       if ( proc_nanosleep != NULL )
+               return;
 
-// Lookup of coresymbolication functions in host dyld.
-static void findCSProcs() {
        struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo());
        const struct mach_header* hostDyldMH = imageInfo->dyldImageLoadAddress;
 
@@ -539,30 +596,45 @@ static void findCSProcs() {
        for (const macho_nlist* s = localsStart; s < localsEnd; ++s) {
                if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
                        const char* name = &symbolTableStrings[s->n_un.n_strx];
-                       if ( strcmp(name, "__Z28coresymbolication_load_imageP25CSCppDyldSharedMemoryPagePK11ImageLoadery") == 0 )
-                               sLoadPtr = (LoadFuncPtr)(s->n_value + slide);
-                       else if ( strcmp(name, "__Z30coresymbolication_unload_imageP25CSCppDyldSharedMemoryPagePK11ImageLoader") == 0 )
-                               sUnloadPtr = (UnloadFuncPtr)(s->n_value + slide);
+                       if ( strcmp(name, "_nanosleep") == 0 )
+                               proc_nanosleep = (FuncPtr_nanosleep)(s->n_value + slide);
+                       else if ( strcmp(name, "_mach_port_allocate") == 0 )
+                               proc_mach_port_allocate = (FuncPtr_mach_port_allocate)(s->n_value + slide);
+                       else if ( strcmp(name, "_mach_msg") == 0 )
+                               proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide);
+                       else if ( strcmp(name, "_kill") == 0 )
+                               proc_kill = (FuncPtr_kill)(s->n_value + slide);
+                       else if ( strcmp(name, "_getpid") == 0 )
+                               proc_getpid = (FuncPtr_getpid)(s->n_value + slide);
+                       else if ( strcmp(name, "_OSAtomicCompareAndSwap32") == 0 )
+                               proc_OSAtomicCompareAndSwap32 = (FuncPtr_OSAtomicCompareAndSwap32)(s->n_value + slide);
                }
        }
 }
+#endif
 
-//void coresymbolication_unload_image(void*, const ImageLoader*);
-void _Z28coresymbolication_load_imagePvPK11ImageLoadery(void* shm, void* image, uint64_t time) {
-       // look up function in host dyld just once
-       if ( sLoadPtr == NULL ) 
-               findCSProcs();
-       if ( sLoadPtr != NULL ) 
-               (*sLoadPtr)(shm, image, time);
-} 
+void xcoresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+{
+       // if host dyld supports this notifier, call into host dyld
+       if ( gSyscallHelpers->version >= 4 )
+               return gSyscallHelpers->coresymbolication_load_notifier(connection, timestamp, path, mh);
+#if SUPPORT_HOST_10_10
+       // otherwise use notifier code in dyld_sim
+       findHostFunctions();
+       coresymbolication_load_notifier(connection, timestamp, path, mh);
+#endif
+}
 
-//void coresymbolication_load_image(void**, const ImageLoader*, uint64_t);
-void _Z30coresymbolication_unload_imagePvPK11ImageLoader(void* shm, void* image) {
-       // look up function in host dyld just once
-       if ( sUnloadPtr == NULL ) 
-               findCSProcs();
-       if ( sUnloadPtr != NULL ) 
-               (*sUnloadPtr)(shm, image);
+void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+{
+       // if host dyld supports this notifier, call into host dyld
+       if ( gSyscallHelpers->version >= 4 )
+               return gSyscallHelpers->coresymbolication_unload_notifier(connection, timestamp, path, mh);
+#if SUPPORT_HOST_10_10
+       // otherwise use notifier code in dyld_sim
+       findHostFunctions();
+       coresymbolication_unload_notifier(connection, timestamp, path, mh);
+#endif
 }
 
 
diff --git a/src/libdyld_data_symbols.dirty b/src/libdyld_data_symbols.dirty
new file mode 100644 (file)
index 0000000..85d1a92
--- /dev/null
@@ -0,0 +1,12 @@
+__ZL12sGlobalMutex
+__ZZ26NSVersionOfLinkTimeLibraryE2mh
+__ZZ33_dyld_register_func_for_add_imageE1p
+__ZZ36_dyld_register_func_for_remove_imageE1p
+__ZZ21_dyld_get_image_slideE1p
+__ZZ6dlopenE1p
+__ZZ40dyld_register_image_state_change_handlerE1p
+__ZZ21_dyld_fast_stub_entryPvlE1p
+__ZZ34dyld_image_path_containing_addressE1p
+__ZZ20_NSGetExecutablePathE1p
+_myDyldSection
+_tlv_terminators_key
index b00937e2cc5147295d94b04049a5a22c2f58bfb5..de2d90be5109bcfcb6ad7b55e73ab4f75c28597a 100644 (file)
@@ -180,9 +180,9 @@ _stub_binding_helper:
 #endif
 
 
-#if __arm__
+#if __arm__ && !__ARM_ARCH_7K__
 /*
- * This is the interface for the stub_binding_helper for ARM:
+ * This is the interface for the old stub_binding_helper for ARM:
  * The caller has pushed the address of the a lazy pointer to be filled in with
  * the value for the defined symbol and pushed the address of the the mach
  * header this pointer comes from.
index 625c320c4c33a047772a6f06928e7bc77508963d..683c5a8ec3ec7154dc2ff8db29963684012dfc42 100644 (file)
@@ -280,24 +280,34 @@ LlazyAllocate:
 
 #endif
 
-#if 0
 #if __arm__
        // returns address of TLV in r0, all other registers preserved
        .globl _tlv_get_addr
        .private_extern _tlv_get_addr
 _tlv_get_addr:
-       push    {r1,r2,r3,r7,lr}                                
-       mov             r7,r0                                                   // save descriptor in r7
+       push    {r1,r2,r3,r7,lr}
+#if __ARM_ARCH_7K__
+       sub             sp, sp, #12                                             // align stack to 16 bytes
+#endif
+       mov             r7, r0                                                  // save descriptor in r7
        ldr             r0, [r7, #4]                                    // get key from descriptor
        bl              _pthread_getspecific                    // get thread value
        cmp             r0, #0
        bne             L2                                                              // if NULL, lazily allocate
+#if __ARM_ARCH_7K__
+       vpush   {d0, d1, d2, d3, d4, d5, d6, d7}
+#endif
        ldr             r0, [r7, #4]                                    // get key from descriptor
        bl              _tlv_allocate_and_initialize_for_key
+#if __ARM_ARCH_7K__
+       vpop    {d0, d1, d2, d3, d4, d5, d6, d7}
+#endif
 L2:    ldr             r1, [r7, #8]                                    // get offset from descriptor
        add             r0, r1, r0                                              // add offset into allocation block
-       pop             {r1,r2,r3,r7,pc}
+#if __ARM_ARCH_7K__
+       add             sp, sp, #12
 #endif
+       pop             {r1,r2,r3,r7,pc}
 #endif
 
        .subsections_via_symbols
index 01a73eb4602bd68cd2dd5dfa22b36165633c2701..e0e2884593d9b126a832139976db8683c55f6a66 100644 (file)
@@ -78,7 +78,7 @@ typedef void (*TermFunc)(void*);
 
 
 
-#if __has_feature(tls) || __arm64__
+#if __has_feature(tls) || __arm64__ || __arm__
 
 typedef struct TLVHandler {
        struct TLVHandler *next;
index 93cb583f8df889df0fe386b659a0feb092d841cd..f597f136628effdea00c2f70d0177e9b79713115 100755 (executable)
@@ -17,7 +17,7 @@ echo "#!/bin/sh" > /var/root/testing/run-all-tests
 chmod +x /var/root/testing/run-all-tests
 
 # do every combination of OS version and architectures
-for os in "7.0" 
+for os in "8.0" 
 do
        for arch in armv7 arm64
        do
index 40cfde7bdb6fe64594949a7a4975f4f8632fe78a..3d6535ad82fbc5fc67c357493acf85e35bab4d8b 100644 (file)
@@ -24,15 +24,18 @@ IOSROOT     =
 
 ifeq "$(OS_NAME)" "iPhoneOS"
        #IOSROOT                = $(shell xcodebuild -version -sdk iphoneos.internal Path)
-       IOSROOT         = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.0.Internal.sdk
+       IOSROOT         = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.Internal.sdk
        CC                      = $(shell xcrun -sdk iphoneos.internal -find cc)  -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT)
        CXX                     = $(shell xcrun -sdk iphoneos.internal -find c++) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT)
        LIPO            = $(shell xcrun -sdk iphoneos.internal -find lipo)
        STRIP           = $(shell xcrun -sdk iphoneos.internal -find strip)
        INSTALL_NAME_TOOL = $(shell xcrun -sdk iphoneos.internal -find install_name_tool)
 else
-       CC                      = $(shell xcrun -find cc)  -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION)
-       CXX                     = $(shell xcrun -find c++) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION)
+  ifeq "$(OSX_SDK_ROOT)" ""
+    OSX_SDK_ROOT               = $(shell xcodebuild -version -sdk macosx.internal Path)
+  endif
+       CC                      = $(shell xcrun -find cc)  -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) -isysroot $(OSX_SDK_ROOT)
+       CXX                     = $(shell xcrun -find c++) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) -isysroot $(OSX_SDK_ROOT)
        LIPO            = $(shell xcrun -find lipo)
        STRIP           = $(shell xcrun -find strip)
        INSTALL_NAME_TOOL = $(shell xcrun -find install_name_tool)
index d1df892e859bbf1efaa0d82a2428715ce94de330..397178c60cdd80de3e06ede6384d15a3144a2a2f 100755 (executable)
@@ -7,6 +7,8 @@ cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'`
 CRSTATE=`defaults read com.apple.CrashReporter DialogType`
 defaults write com.apple.CrashReporter DialogType basic
 
+SDK=`xcodebuild -version -sdk macosx.internal Path`
+
 # run test targeting different OS versions
 for OSVERSION in 10.9 10.8 10.7 10.6 10.5 10.4
 do 
@@ -17,7 +19,7 @@ do
        ../bin/make-recursive.pl clean > /dev/null
 
        # build default architecture
-       ../bin/make-recursive.pl ARCH="i386" OS_VERSION=$OSVERSION OS_NAME=MacOSX | ../bin/result-filter.pl
+       ../bin/make-recursive.pl ARCH="i386" OS_VERSION=$OSVERSION OS_NAME=MacOSX OSX_SDK_ROOT=${SDK} | ../bin/result-filter.pl
 
        # if 64-bit capable Intel, then also run all test cases for 64-bits
        if [ `sysctl -n hw.optional.x86_64` = "1" ] 
@@ -29,7 +31,7 @@ do
                ../bin/make-recursive.pl clean > /dev/null
 
                # build x86_64 architecture
-               ../bin/make-recursive.pl ARCH="x86_64" OS_VERSION=$OSVERSION OS_NAME=MacOSX | ../bin/result-filter.pl
+               ../bin/make-recursive.pl ARCH="x86_64" OS_VERSION=$OSVERSION OS_NAME=MacOSX OSX_SDK_ROOT=${SDK} | ../bin/result-filter.pl
        fi
 
 done
index a43882fe5f951bfff07cb41a906ff13518185853..26a1e09f8bad13a11ee4cda1d240c6b74185f939 100644 (file)
@@ -35,7 +35,6 @@ extern int foo();
 
 int main(int argc, const char* argv[])
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
        if ( argc > 2 ) {
                bool found = false;
                uint32_t count = _dyld_image_count();
@@ -57,7 +56,6 @@ int main(int argc, const char* argv[])
        if ( actualResult != expectedResult )
                FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
        else
-#endif
                PASS("DYLD_VERSIONED_FRAMEWORK_PATH-basic");
                
        return EXIT_SUCCESS;
index 224d93e8527e7a64f070ef5878d9d8b5daea06bd..a903b04b7af5f87d08bf09d77ae989c26cd38043 100644 (file)
@@ -34,14 +34,12 @@ extern int foo();
 
 int main(int argc, const char* argv[])
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
        int expectedResult = atoi(argv[1]);
        int actualResult = foo();
        //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult);
        if ( actualResult != expectedResult )
                FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
        else
-#endif
                PASS("DYLD_VERSIONED_LIBRARY_PATH-basic");
                
        return EXIT_SUCCESS;
index 224d93e8527e7a64f070ef5878d9d8b5daea06bd..a903b04b7af5f87d08bf09d77ae989c26cd38043 100644 (file)
@@ -34,14 +34,12 @@ extern int foo();
 
 int main(int argc, const char* argv[])
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
        int expectedResult = atoi(argv[1]);
        int actualResult = foo();
        //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult);
        if ( actualResult != expectedResult )
                FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
        else
-#endif
                PASS("DYLD_VERSIONED_LIBRARY_PATH-basic");
                
        return EXIT_SUCCESS;
index 224d93e8527e7a64f070ef5878d9d8b5daea06bd..a903b04b7af5f87d08bf09d77ae989c26cd38043 100644 (file)
@@ -34,14 +34,12 @@ extern int foo();
 
 int main(int argc, const char* argv[])
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
        int expectedResult = atoi(argv[1]);
        int actualResult = foo();
        //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult);
        if ( actualResult != expectedResult )
                FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
        else
-#endif
                PASS("DYLD_VERSIONED_LIBRARY_PATH-basic");
                
        return EXIT_SUCCESS;
diff --git a/unit-tests/test-cases/image_header_containing_address/Makefile b/unit-tests/test-cases/image_header_containing_address/Makefile
new file mode 100644 (file)
index 0000000..a9c79c1
--- /dev/null
@@ -0,0 +1,38 @@
+##
+# Copyright (c) 2009 Apple 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
+
+all-check: all check
+
+check:
+       ./main
+
+all:
+       ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib
+       ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib
+       
+
+clean:
+       ${RM} ${RMFLAGS} *~ main libfoo.dylib
+
diff --git a/unit-tests/test-cases/image_header_containing_address/foo.c b/unit-tests/test-cases/image_header_containing_address/foo.c
new file mode 100644 (file)
index 0000000..85e6cd8
--- /dev/null
@@ -0,0 +1 @@
+void foo() {}
diff --git a/unit-tests/test-cases/image_header_containing_address/main.c b/unit-tests/test-cases/image_header_containing_address/main.c
new file mode 100644 (file)
index 0000000..56312cd
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015 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 <string.h> 
+#include <dlfcn.h> 
+#include <mach-o/dyld.h> 
+#include <mach-o/dyld_priv.h> 
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+extern void foo();
+
+
+// checks dladdr() and dyld_image_header_containing_address() return same image
+static void verify(void* addr)
+{
+       Dl_info info;
+       if ( dladdr(addr, &info) == 0 ) {
+               FAIL("dladdr(%p, xx) failed", addr);
+               exit(0);
+       }
+       const struct mach_header* mh = dyld_image_header_containing_address(addr);
+       if ( mh != info.dli_fbase ) {
+               FAIL("dladdr's  dli_fbase != dyld_image_header_containing_address()");
+               exit(0);
+       }
+}
+
+
+int main()
+{
+       verify(&main);
+       verify(&foo);
+       
+       int x;
+       if ( dyld_image_path_containing_address(&x) != NULL ) {
+               FAIL("dyld_image_header_containing_address() of stack variable not NULL");
+               exit(0);
+       }
+  
+       PASS("dyld_image_header_containing_address");
+       return EXIT_SUCCESS;
+}
diff --git a/unit-tests/test-cases/interpose-not-inserted/Makefile b/unit-tests/test-cases/interpose-not-inserted/Makefile
new file mode 100644 (file)
index 0000000..cccf28c
--- /dev/null
@@ -0,0 +1,42 @@
+##
+# Copyright (c) 2005-2007 Apple 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
+
+all-check: all check
+
+check:
+        ./main
+
+all: main libmystrdup.dylib
+
+main : main.c wrap.c mystrdup.c
+       ${CC} ${CCFLAGS} -dynamiclib mystrdup.c -o libmystrdup.dylib
+       ${CC} ${CCFLAGS} -dynamiclib wrap.c -o libwrap.dylib
+       ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main libwrap.dylib libmystrdup.dylib main.c
+
+
+
+clean:
+       ${RM} ${RMFLAGS} *~ main libmystrdup.dylib libwrap.dylib
+
diff --git a/unit-tests/test-cases/interpose-not-inserted/main.c b/unit-tests/test-cases/interpose-not-inserted/main.c
new file mode 100644 (file)
index 0000000..c71ffb6
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2005-2007 Apple 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 <string.h>
+#include <dlfcn.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+extern char* wrap_strdup(const char*);
+
+int main()
+{
+       const char* x = strdup("123");
+       const char* y = wrap_strdup("456");
+  
+       if ( (strcmp(x, "hello") == 0) && (strcmp(y, "hello") == 0) )
+               PASS("interpose-not-inserted");
+       else
+               FAIL("interpose-not-inserted");
+       return EXIT_SUCCESS;
+}
diff --git a/unit-tests/test-cases/interpose-not-inserted/mystrdup.c b/unit-tests/test-cases/interpose-not-inserted/mystrdup.c
new file mode 100644 (file)
index 0000000..18041ac
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 <string.h>
+#include <mach-o/dyld-interposing.h>
+
+char* mystrdup(const char* in)
+{
+       return strdup("hello");
+}
+
+DYLD_INTERPOSE(mystrdup, strdup)
diff --git a/unit-tests/test-cases/interpose-not-inserted/wrap.c b/unit-tests/test-cases/interpose-not-inserted/wrap.c
new file mode 100644 (file)
index 0000000..f634ab2
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2007 Apple 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 <string.h>
+
+// make a pointer statically initiallized to strdup()
+// since libwrap.dylib is staticlly linked to main
+// this verfies that interposing happens properly
+static char* (*proc)(const char*) = strdup;
+
+char* wrap_strdup(const char* str)
+{
+       return proc(str);
+}
+