+++ /dev/null
-#include "<DEVELOPER_DIR>/AppleInternal/XcodeConfig/SimulatorSupport.xcconfig"
-
-// Set INSTALL_PATH[sdk=macosx*] when SimulatorSupport.xcconfig is unavailable
-INSTALL_PATH[sdk=macosx*] = $(INSTALL_PATH_ACTUAL)
--- /dev/null
+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
--- /dev/null
+
+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
+
+
--- /dev/null
+
+//: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
+
+++ /dev/null
-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
/* 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)",
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
#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
#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
//
-// 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
//
-// 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
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.
//
// (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;
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];
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;
S32(occupied);
S32(shift);
S32(mask);
- S32(zero);
S64(salt);
}
occupied = phash.occupied;
shift = phash.shift;
mask = phash.mask;
- zero = 0;
- unused = 0;
+ unused1 = 0;
+ unused2 = 0;
salt = phash.salt;
if (size() > remaining) {
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++) {
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
return "class list busted (metadata not optimized)";
}
- if (classOffsets()[h].clsOffset != zeroOffset) {
+ if (classOffsets()[h].clsOffset != 0) {
// already did this class
continue;
}
#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;
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;
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
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.
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;
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;
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);
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();
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;
const macho_dyld_info_command<P>* fDyldInfo;
bool fOriginallyPrebound;
bool fReExportedSymbolsResolved;
- std::vector<ClientAndSymbol> fClientAndSymbols;
- NameToAddrMap fResolverLazyPointers;
+ ResolverClientsMap fResolverInfo;
};
template <>
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)
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 )
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);
}
}
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;
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>
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 = §ionsStart[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>
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 = §ionsStart[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;
}
}
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);
}
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();
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;
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());
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());
}
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);
// 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()];
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;
}
#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"
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());
}
}
}
+ if (strcmp(segname, "__DATA") == 0)
+ return getSection("__DATA_CONST", sectname);
return NULL;
}
#include <unistd.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
+#include <rootless.h>
#include <vector>
#include <set>
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);
}
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; }
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;
uint64_t fFileSize;
uint64_t fAlignment;
uint32_t fPermissions;
+ uint32_t fSectionCount;
+ uint64_t fSectionsSize;
+ uint64_t fSectionsAlignment;
uint64_t fNewAddress;
void* fMappedAddress;
};
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;
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; }
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();
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;
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 = §ionsStart[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 = §ionsStart[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)
{
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;
}
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:
}
}
+ 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; }
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;
};
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;
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);
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() ) {
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 = §ionsStart[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; }
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()
{
// 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>));
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 = §ionsStart[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()
{
}
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 {
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;
+ }
}
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;
{
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 = §ionsStart[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;
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());
}
}
-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 = §ionsStart[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__
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)
{
// 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);
}
}
};
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>
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)); }
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>
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 {
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 {
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> *>
}
}
- // 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();
+ }
+ }
}
};
// 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
};
+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 {
uint32_t fOptimized;
+ GCClassSet<A> fGCClasses;
+
public:
IvarOffsetOptimizer() : fOptimized(0) { }
// 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
}
}
}
-
+
+ // 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);
}
}
};
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) { }
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);
}
};
{
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)";
}
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;
}
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 )
return true;
return false;
}
-private:
const std::set<int> &_reexportDeps;
};
// 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;
}
segInfo.name = segCmd->segname();
segInfo.fileOffset = fileOffset;
segInfo.fileSize = sizem;
+ if ( segCmd->filesize() > segCmd->vmsize() )
+ return -1;
segInfo.address = segCmd->vmaddr();
callback(&dylibInfo, &segInfo);
}
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 {
#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
static bool verbose = false;
static bool progress = false;
static bool iPhoneOS = false;
+static bool rootless = true;
static std::vector<const char*> warnings;
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; }
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;
}
+
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;
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 {
// 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)
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;
StringPool::StringPool()
- : fBufferUsed(0), fBufferAllocated(64*1024*1024)
+ : fBufferUsed(0), fBufferAllocated(128*1024*1024)
{
fBuffer = (char*)malloc(fBufferAllocated);
}
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
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,
{
}
- 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;
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.
// 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);
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());
}
}
}
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:
};
+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;
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;
}
}
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();
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.
}
+ // 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);
// 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 ) {
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());
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>
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 ) {
::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;
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
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();
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;
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();
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);
}
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);
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 = §ionsStart[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 {
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;
// 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>)];
// 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);
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 ) {
}
}
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");
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) {
}
}
fclose(fmap);
- result = ::rename(tempMapFilePath, mapFilePath);
+ ::rename(tempMapFilePath, mapFilePath);
}
}
}
}
catch (...){
- // remove temp cache file
- ::unlink(tempCachePath);
// remove in memory cache
if ( inMemoryCache != NULL )
vm_deallocate(mach_task_self(), (vm_address_t)inMemoryCache, allocatedCacheSize);
--- /dev/null
+<?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>
+++ /dev/null
-
-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
-
-
}
}
}
- 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 )
#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
bool linkingMainExecutable;
bool startedInitializingMainExecutable;
bool processIsRestricted;
+ bool processRequiresLibraryValidation;
bool verboseOpts;
bool verboseEnv;
bool verboseMapping;
#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
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),
// 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);
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 = §ionsStart[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:
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);
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);
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
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);
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);
}
-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) ) {
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:
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
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);
}
}
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;
}
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;
}
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;
}
}
}
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;
}
}
}
}
- 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;
}
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 = §ionsStart[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;
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;
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;
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;
// 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);
}
// 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)
// 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);
// 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 ) {
// for compatibility, never unload dylibs loaded from memory
image->setNeverUnload();
+ image->disableCoverageCheck();
+
// bundle loads need path copied
if ( moduleName != NULL )
image->setPath(moduleName);
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()
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);
*/
+#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"
// 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) {
}
// 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);
// 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();
image->fInSharedCache = true;
image->setNeverUnload();
image->setSlide(slide);
+ image->disableCoverageCheck();
// segments already mapped in cache
if ( context.verboseMapping ) {
// for compatibility, never unload dylibs loaded from memory
image->setNeverUnload();
+ image->disableCoverageCheck();
+
// bundle loads need path copied
if ( moduleName != NULL )
image->setPath(moduleName);
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
}
+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
+}
+
+
+
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,
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;
};
#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
// 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 */
#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;
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
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";
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:
}
}
}
- // 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());
}
}
}
}
}
}
+ 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);
- }
- }
- }
- }
}
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)
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 )
//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) ) {
{
//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;
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);
}
{
//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;
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);
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];
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);
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 = §ionsStart[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__
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
}
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);
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
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;
}
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)];
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);
}
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];
// 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 ) {
}
-#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.
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;
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 )
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;
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) {
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;
}
}
-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;
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,
// added in version 3
&opendir,
&readdir_r,
- &closedir
+ &closedir,
+ // added in version 4
+ &coresymbolication_load_notifier,
+ &coresymbolication_unload_notifier
};
__attribute__((noinline))
// 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:
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
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;
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);
}
// <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
++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);
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) ) {
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)
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);
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);
}
{"__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 },
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);
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) )
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);
#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 */
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 )
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
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:
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;
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 },
{ 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 ) {
}
}
-#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
{ 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 ) {
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()
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;
}
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;
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)
{
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;
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);
}
}
__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)
#define __DYLD_SYSCALL_HELPERS__
#include <dirent.h>
+#include <mach-o/loader.h>
#if __cplusplus
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;
#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
#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;
//
#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);
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;
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;
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
}
--- /dev/null
+__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
#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.
#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
-#if __has_feature(tls) || __arm64__
+#if __has_feature(tls) || __arm64__ || __arm__
typedef struct TLVHandler {
struct TLVHandler *next;
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
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)
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
../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" ]
../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
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();
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;
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;
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;
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;
--- /dev/null
+##
+# 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
+
--- /dev/null
+void foo() {}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+##
+# 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
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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);
+}
+