From df9d6cf7fb01f4e3d4128fe7a687984fe0b45584 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 4 Sep 2015 17:27:27 +0000 Subject: [PATCH] dyld-360.14.tar.gz --- base.xcconfig | 4 - configs/base.xcconfig | 0 dyld.xcconfig => configs/dyld.xcconfig | 2 +- configs/libdyld.xcconfig | 8 + configs/update_dyld_shared_cache.xcconfig | 18 + dyld.xcodeproj/project.pbxproj | 92 ++- include/mach-o/dyld_priv.h | 40 +- include/objc-shared-cache.h | 131 ++- launch-cache/CacheFileAbstraction.hpp | 3 + launch-cache/MachOBinder.hpp | 519 ++---------- launch-cache/MachOFileAbstraction.hpp | 22 +- launch-cache/MachOLayout.hpp | 87 +- launch-cache/MachORebaser.hpp | 776 +++++++++--------- launch-cache/ObjCLegacyAbstraction.hpp | 9 +- launch-cache/ObjCModernAbstraction.hpp | 350 ++++++-- launch-cache/dsc_extractor.cpp | 17 +- launch-cache/dsc_iterator.cpp | 2 + launch-cache/dyld_cache_format.h | 1 + launch-cache/update_dyld_shared_cache.cpp | 687 ++++++++++++---- ...pdate_dyld_shared_cache_entitlements.plist | 8 + libdyld.xcconfig | 8 - src/ImageLoader.cpp | 5 +- src/ImageLoader.h | 5 +- src/ImageLoaderMachO.cpp | 235 ++++-- src/ImageLoaderMachO.h | 15 +- src/ImageLoaderMachOClassic.cpp | 13 +- src/ImageLoaderMachOClassic.h | 2 +- src/ImageLoaderMachOCompressed.cpp | 58 +- src/ImageLoaderMachOCompressed.h | 9 +- src/dyld.cpp | 541 +++++++----- src/dyld.h | 1 + src/dyldAPIs.cpp | 22 +- src/dyldAPIsInLibSystem.cpp | 234 ++++-- src/dyldNew.cpp | 12 +- src/dyldStartup.s | 2 +- src/dyldSyscallInterface.h | 4 + src/dyld_stub_binder.s | 2 + src/glue.c | 122 ++- src/libdyld_data_symbols.dirty | 12 + src/stub_binding_helper.s | 4 +- src/threadLocalHelpers.s | 18 +- src/threadLocalVariables.c | 2 +- unit-tests/build-iPhoneOS-unit-tests | 2 +- unit-tests/include/common.makefile | 9 +- unit-tests/run-all-unit-tests | 6 +- .../main.c | 2 - .../DYLD_VERSIONED_LIBRARY_PATH-basic/main.c | 2 - .../main.c | 2 - .../main.c | 2 - .../image_header_containing_address/Makefile | 38 + .../image_header_containing_address/foo.c | 1 + .../image_header_containing_address/main.c | 65 ++ .../interpose-not-inserted/Makefile | 42 + .../test-cases/interpose-not-inserted/main.c | 42 + .../interpose-not-inserted/mystrdup.c | 32 + .../test-cases/interpose-not-inserted/wrap.c | 35 + 56 files changed, 2873 insertions(+), 1509 deletions(-) delete mode 100644 base.xcconfig create mode 100644 configs/base.xcconfig rename dyld.xcconfig => configs/dyld.xcconfig (95%) create mode 100644 configs/libdyld.xcconfig create mode 100644 configs/update_dyld_shared_cache.xcconfig create mode 100644 launch-cache/update_dyld_shared_cache_entitlements.plist delete mode 100644 libdyld.xcconfig create mode 100644 src/libdyld_data_symbols.dirty create mode 100644 unit-tests/test-cases/image_header_containing_address/Makefile create mode 100644 unit-tests/test-cases/image_header_containing_address/foo.c create mode 100644 unit-tests/test-cases/image_header_containing_address/main.c create mode 100644 unit-tests/test-cases/interpose-not-inserted/Makefile create mode 100644 unit-tests/test-cases/interpose-not-inserted/main.c create mode 100644 unit-tests/test-cases/interpose-not-inserted/mystrdup.c create mode 100644 unit-tests/test-cases/interpose-not-inserted/wrap.c diff --git a/base.xcconfig b/base.xcconfig deleted file mode 100644 index 91782c7..0000000 --- a/base.xcconfig +++ /dev/null @@ -1,4 +0,0 @@ -#include "/AppleInternal/XcodeConfig/SimulatorSupport.xcconfig" - -// Set INSTALL_PATH[sdk=macosx*] when SimulatorSupport.xcconfig is unavailable -INSTALL_PATH[sdk=macosx*] = $(INSTALL_PATH_ACTUAL) diff --git a/configs/base.xcconfig b/configs/base.xcconfig new file mode 100644 index 0000000..e69de29 diff --git a/dyld.xcconfig b/configs/dyld.xcconfig similarity index 95% rename from dyld.xcconfig rename to configs/dyld.xcconfig index 7c6bbfe..fc7bcdc 100644 --- a/dyld.xcconfig +++ b/configs/dyld.xcconfig @@ -17,4 +17,4 @@ PRODUCT_NAME[sdk=*simulator*] = dyld_sim PRODUCT_NAME[sdk=iphoneos*] = dyld PRODUCT_NAME[sdk=macosx*] = dyld -INSTALL_PATH_ACTUAL = /usr/lib +INSTALL_PATH = /usr/lib diff --git a/configs/libdyld.xcconfig b/configs/libdyld.xcconfig new file mode 100644 index 0000000..756db3d --- /dev/null +++ b/configs/libdyld.xcconfig @@ -0,0 +1,8 @@ + +LIBSYSTEM_LIBS[sdk=*simulator*] = -Wl,-upward-lsystem_sim_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_sim_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_sim_kernel +LIBSYSTEM_LIBS[sdk=iphoneos*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel +LIBSYSTEM_LIBS[sdk=macosx*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel + +INSTALL_PATH = /usr/lib/system + + diff --git a/configs/update_dyld_shared_cache.xcconfig b/configs/update_dyld_shared_cache.xcconfig new file mode 100644 index 0000000..1bed864 --- /dev/null +++ b/configs/update_dyld_shared_cache.xcconfig @@ -0,0 +1,18 @@ + +//:configuration = Release +LOCAL_YES = local + +//:configuration = Release +LOCAL = $(LOCAL_$(RC_PURPLE)) + +//:configuration = Release +INSTALL_PATH = $(INSTALL_LOCATION)/usr/$(LOCAL)/bin + +// don't iOS tool +MY_RELEASE_CODE_SIGN_IDENTITY_YES = +MY_RELEASE_CODE_SIGN_IDENTITY_ = - +CODE_SIGN_IDENTITY = $(MY_RELEASE_CODE_SIGN_IDENTITY_$(RC_PURPLE)) + + +CODE_SIGN_ENTITLEMENTS = launch-cache/update_dyld_shared_cache_entitlements.plist + diff --git a/dyld.xcodeproj/project.pbxproj b/dyld.xcodeproj/project.pbxproj index c0dde47..e17ce86 100644 --- a/dyld.xcodeproj/project.pbxproj +++ b/dyld.xcodeproj/project.pbxproj @@ -303,7 +303,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 3FC074DF188330C8005F11DD /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; 834A90AB0E1D85D600555761 /* ObjCLegacyAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCLegacyAbstraction.hpp; sourceTree = ""; }; 834A90AC0E1D85D600555761 /* ObjCModernAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCModernAbstraction.hpp; sourceTree = ""; }; EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; }; @@ -313,6 +312,7 @@ 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 = ""; }; F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = ""; }; F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = ""; }; F93666DF163B4C42002ECADA /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; @@ -330,15 +330,18 @@ F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = ""; }; F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = ""; }; F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = ""; }; + F971DD131A4A0E0700BBDD52 /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; + F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = ""; }; + F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = ""; }; + F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = ""; }; F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = ""; }; - F97988FA187F706600EC2C8E /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = ""; }; - F97988FB187F707A00EC2C8E /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = ""; }; F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = ""; }; F98935B90A9A412B00FB6228 /* MachOBinder.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOBinder.hpp; sourceTree = ""; }; F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachORebaser.hpp; sourceTree = ""; }; F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = ""; }; F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = ""; }; 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 = ""; }; F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = ""; }; F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = dyld_stub_binder.s; path = src/dyld_stub_binder.s; sourceTree = ""; }; F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = ""; }; @@ -358,7 +361,7 @@ 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; }; @@ -453,6 +456,7 @@ F939373D0A94FC4700070A07 /* launch-cache */ = { isa = PBXGroup; children = ( + F90C540A1A82D78000558E8C /* update_dyld_shared_cache_entitlements.plist */, F93666E1163B4C58002ECADA /* Security.framework */, F93666DF163B4C42002ECADA /* CoreFoundation.framework */, F939373E0A94FC4700070A07 /* Architectures.hpp */, @@ -476,12 +480,21 @@ path = "launch-cache"; sourceTree = ""; }; + F971DD121A4A0E0700BBDD52 /* configs */ = { + isa = PBXGroup; + children = ( + F971DD131A4A0E0700BBDD52 /* base.xcconfig */, + F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */, + F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */, + F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */, + ); + path = configs; + sourceTree = ""; + }; F9ED4C870630A72200DF4E74 = { isa = PBXGroup; children = ( - 3FC074DF188330C8005F11DD /* base.xcconfig */, - F97988FA187F706600EC2C8E /* dyld.xcconfig */, - F97988FB187F707A00EC2C8E /* libdyld.xcconfig */, + F971DD121A4A0E0700BBDD52 /* configs */, F9ED4CBB0630A7AA00DF4E74 /* src */, F9ED4CC30630A7BE00DF4E74 /* doc */, F9ED4CBE0630A7B100DF4E74 /* include */, @@ -534,6 +547,7 @@ 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 */, @@ -730,7 +744,7 @@ ); 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 */ = { @@ -746,7 +760,7 @@ ); 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 */ = { @@ -761,7 +775,7 @@ ); 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 */ = { @@ -775,8 +789,8 @@ 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 */ = { @@ -791,7 +805,7 @@ ); 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 */ = { @@ -973,6 +987,7 @@ F93937350A94FB2900070A07 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; CLANG_CXX_LIBRARY = "libc++"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; @@ -990,17 +1005,20 @@ 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++"; @@ -1013,15 +1031,14 @@ 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"; @@ -1038,7 +1055,7 @@ 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; @@ -1051,7 +1068,7 @@ 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; }; @@ -1068,7 +1085,7 @@ 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++", @@ -1092,7 +1109,7 @@ 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++", @@ -1109,7 +1126,7 @@ }; F9D8C7DE087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F97988FA187F706600EC2C8E /* dyld.xcconfig */; + baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; @@ -1148,6 +1165,7 @@ "$(ALIGNMENT)", "$(ENTRY)", ); + SDKROOT = macosx.internal; STRIPFLAGS = "-S"; UNSTRIPPED_PRODUCT = NO; VERSIONING_SYSTEM = "apple-generic"; @@ -1160,7 +1178,7 @@ }; F9D8C7E0087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F97988FA187F706600EC2C8E /* dyld.xcconfig */; + baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */; buildSettings = { CLANG_CXX_LIBRARY = "libc++"; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -1195,6 +1213,7 @@ "$(ALIGNMENT)", "$(ENTRY)", ); + SDKROOT = macosx.internal; STRIPFLAGS = "-S"; UNSTRIPPED_PRODUCT = NO; VERSIONING_SYSTEM = "apple-generic"; @@ -1208,7 +1227,7 @@ }; F9D8C7E2087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F97988FB187F707A00EC2C8E /* libdyld.xcconfig */; + baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; DEAD_CODE_STRIPPING = YES; @@ -1220,6 +1239,7 @@ 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)", @@ -1239,7 +1259,7 @@ }; F9D8C7E4087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F97988FB187F707A00EC2C8E /* libdyld.xcconfig */; + baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */; buildSettings = { COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; @@ -1254,6 +1274,7 @@ 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)", @@ -1265,6 +1286,21 @@ 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; @@ -1294,7 +1330,7 @@ }; F9D8C7EA087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3FC074DF188330C8005F11DD /* base.xcconfig */; + baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */; buildSettings = { CLANG_CXX_LIBRARY = "compiler-default"; }; @@ -1302,7 +1338,7 @@ }; F9D8C7EC087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3FC074DF188330C8005F11DD /* base.xcconfig */; + baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */; buildSettings = { CLANG_CXX_LIBRARY = "compiler-default"; }; @@ -1318,7 +1354,7 @@ 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)", @@ -1345,7 +1381,7 @@ 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)", diff --git a/include/mach-o/dyld_priv.h b/include/mach-o/dyld_priv.h index 24208db..7708927 100644 --- a/include/mach-o/dyld_priv.h +++ b/include/mach-o/dyld_priv.h @@ -171,6 +171,14 @@ extern bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* extern const char* dyld_image_path_containing_address(const void* addr); +// +// This is an optimized form of dladdr() that only returns the dli_fbase field. +// Return NULL, if address is not in any image tracked by dyld. +// +// Exists in Mac OS X 10.11 and later +extern const struct mach_header* dyld_image_header_containing_address(const void* addr); + + // Convienence constants for return values from dyld_get_sdk_version() and friends. #define DYLD_MACOSX_VERSION_10_4 0x000A0400 @@ -180,6 +188,7 @@ extern const char* dyld_image_path_containing_address(const void* addr); #define DYLD_MACOSX_VERSION_10_8 0x000A0800 #define DYLD_MACOSX_VERSION_10_9 0x000A0900 #define DYLD_MACOSX_VERSION_10_10 0x000A0A00 +#define DYLD_MACOSX_VERSION_10_11 0x000A0B00 #define DYLD_IOS_VERSION_2_0 0x00020000 #define DYLD_IOS_VERSION_2_1 0x00020100 @@ -198,9 +207,13 @@ extern const char* dyld_image_path_containing_address(const void* addr); #define DYLD_IOS_VERSION_7_0 0x00070000 #define DYLD_IOS_VERSION_7_1 0x00070100 #define DYLD_IOS_VERSION_8_0 0x00080000 +#define DYLD_IOS_VERSION_8_1 0x00080100 +#define DYLD_IOS_VERSION_8_2 0x00080200 +#define DYLD_IOS_VERSION_9_0 0x00090000 + // -// This is finds the SDK version a binary was built against. +// This finds the SDK version a binary was built against. // Returns zero on error, or if SDK version could not be determined. // // Exists in Mac OS X 10.8 and later @@ -209,16 +222,26 @@ extern uint32_t dyld_get_sdk_version(const struct mach_header* mh); // -// This is finds the SDK version the main executable was built against. +// This finds the SDK version that the main executable was built against. // Returns zero on error, or if SDK version could not be determined. // +// Note on WatchOS, this returns the equivalent iOS SDK version number +// (i.e an app built against WatchOS 2.0 SDK returne 9.0). To see the +// platform specific sdk version use dyld_get_program_sdk_watch_os_version(). +// // Exists in Mac OS X 10.8 and later // Exists in iOS 6.0 and later extern uint32_t dyld_get_program_sdk_version(); +// Watch OS only. +// This finds the Watch OS SDK version that the main executable was built against. +// Exists in Watch OS 2.0 and later +extern uint32_t dyld_get_program_sdk_watch_os_version(); // __WATCHOS_AVAILABLE(2.0); + + // -// This is finds the min OS version a binary was built to run on. +// This finds the min OS version a binary was built to run on. // Returns zero on error, or if no min OS recorded in binary. // // Exists in Mac OS X 10.8 and later @@ -227,7 +250,7 @@ extern uint32_t dyld_get_min_os_version(const struct mach_header* mh); // -// This is finds the min OS version the main executable was built to run on. +// This finds the min OS version the main executable was built to run on. // Returns zero on error, or if no min OS recorded in binary. // // Exists in Mac OS X 10.8 and later @@ -253,6 +276,15 @@ extern bool dyld_shared_cache_some_image_overridden(); extern bool dyld_process_is_restricted(); + +// +// Returns path used by dyld for standard dyld shared cache file for the current arch. +// +// Exists in Mac OS X 10.11 and later +extern const char* dyld_shared_cache_file_path(); + + + // // for OpenGL to tell dyld it is ok to deallocate a memory based image when done. // diff --git a/include/objc-shared-cache.h b/include/objc-shared-cache.h index 8d59bfb..d2a27df 100644 --- a/include/objc-shared-cache.h +++ b/include/objc-shared-cache.h @@ -145,6 +145,9 @@ struct hashstr { // (used for selector names and class names) typedef std::unordered_map string_map; +// protocol name => protocol vmaddress +typedef std::unordered_map protocol_map; + // class name => (class vmaddress, header_info vmaddress) typedef std::unordered_multimap, hashstr, eqstr> class_map; @@ -161,8 +164,8 @@ struct objc_stringhash_t { uint32_t occupied; uint32_t shift; uint32_t mask; - uint32_t zero; - uint32_t unused; // alignment pad + uint32_t unused1; // was zero + uint32_t unused2; // alignment pad uint64_t salt; uint32_t scramble[256]; @@ -221,9 +224,8 @@ struct objc_stringhash_t { if (check_fail) return INDEX_NOT_FOUND; #endif - // fixme change &zero to 0 in the next version-breaking update objc_stringhash_offset_t offset = offsets()[h]; - if (offset == offsetof(objc_stringhash_t,zero)) return INDEX_NOT_FOUND; + if (offset == 0) return INDEX_NOT_FOUND; const char *result = (const char *)this + offset; if (0 != strcmp(key, result)) return INDEX_NOT_FOUND; @@ -259,7 +261,6 @@ struct objc_stringhash_t { S32(occupied); S32(shift); S32(mask); - S32(zero); S64(salt); } @@ -284,8 +285,8 @@ struct objc_stringhash_t { occupied = phash.occupied; shift = phash.shift; mask = phash.mask; - zero = 0; - unused = 0; + unused1 = 0; + unused2 = 0; salt = phash.salt; if (size() > remaining) { @@ -300,10 +301,9 @@ struct objc_stringhash_t { tab[i] = phash.tab[i]; } - // Set offsets to "" + // Set offsets to 0 for (uint32_t i = 0; i < phash.capacity; i++) { - offsets()[i] = - (objc_stringhash_offset_t)offsetof(objc_stringhash_t, zero); + offsets()[i] = 0; } // Set checkbytes to 0 for (uint32_t i = 0; i < phash.capacity; i++) { @@ -469,12 +469,10 @@ struct objc_clsopt_t : objc_stringhash_t { return "selector section too small (metadata not optimized)"; } - // Set class offsets to &zero - objc_stringhash_offset_t zeroOffset = - (objc_stringhash_offset_t)offsetof(objc_stringhash_t, zero); + // Set class offsets to 0 for (uint32_t i = 0; i < capacity; i++) { - classOffsets()[i].clsOffset = zeroOffset; - classOffsets()[i].hiOffset = zeroOffset; + classOffsets()[i].clsOffset = 0; + classOffsets()[i].hiOffset = 0; } // Set real class offsets @@ -486,7 +484,7 @@ struct objc_clsopt_t : objc_stringhash_t { return "class list busted (metadata not optimized)"; } - if (classOffsets()[h].clsOffset != zeroOffset) { + if (classOffsets()[h].clsOffset != 0) { // already did this class continue; } @@ -551,6 +549,88 @@ struct objc_clsopt_t : objc_stringhash_t { #endif }; + + +struct objc_protocolopt_t : objc_stringhash_t { + // ...objc_stringhash_t fields... + // uint32_t protocolOffsets[capacity]; /* offsets from &capacity to protocol_t */ + + objc_stringhash_offset_t *protocolOffsets() { return (objc_stringhash_offset_t *)&offsets()[capacity]; } + const objc_stringhash_offset_t *protocolOffsets() const { return (const objc_stringhash_offset_t *)&offsets()[capacity]; } + + void* getProtocol(const char *key) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) { + return NULL; + } + + return (void *)((const char *)this + protocolOffsets()[h]); + } + +#ifdef SELOPT_WRITE + + size_t size() + { + return + objc_stringhash_t::size() + capacity * sizeof(objc_stringhash_offset_t); + } + + void byteswap(bool little_endian) + { + objc_stringhash_offset_t *o; + + o = protocolOffsets(); + for (objc_stringhash_offset_t i = 0; i < capacity; i++) { + S32(o[i]); + } + + objc_stringhash_t::byteswap(little_endian); + } + + const char *write(uint64_t base, size_t remaining, + string_map& strings, protocol_map& protocols, + bool verbose) + { + const char *err; + err = objc_stringhash_t::write(base, remaining, strings); + if (err) return err; + + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // Set protocol offsets to 0 + for (uint32_t i = 0; i < capacity; i++) { + protocolOffsets()[i] = 0; + } + + // Set real protocol offsets +# define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t)) + protocol_map::const_iterator c; + for (c = protocols.begin(); c != protocols.end(); ++c) { + uint32_t h = getIndex(c->first); + if (h == INDEX_NOT_FOUND) { + return "protocol list busted (metadata not optimized)"; + } + + int64_t offset = c->second - base; + if ((offset<>SHIFT != offset) { + return "protocol offset too big (metadata not optimized)"; + } + + protocolOffsets()[h] = (objc_stringhash_offset_t)offset; + } +# undef SHIFT + + return NULL; + } + +// SELOPT_WRITE +#endif +}; + + // Precomputed image list. struct objc_headeropt_t; @@ -558,15 +638,16 @@ struct objc_headeropt_t; struct objc_clsopt_t; // Edit objc-sel-table.s if you change this value. -enum { VERSION = 12 }; +enum { VERSION = 13 }; // Top-level optimization structure. // Edit objc-sel-table.s and OPT_INITIALIZER if you change this structure. -struct objc_opt_t { +struct alignas(alignof(void*)) objc_opt_t { uint32_t version; int32_t selopt_offset; int32_t headeropt_offset; int32_t clsopt_offset; + int32_t protocolopt_offset; const objc_selopt_t* selopt() const { if (selopt_offset == 0) return NULL; @@ -586,6 +667,11 @@ struct objc_opt_t { if (clsopt_offset == 0) return NULL; return (objc_clsopt_t *)((uint8_t *)this + clsopt_offset); } + + struct objc_protocolopt_t* protocolopt() const { + if (protocolopt_offset == 0) return NULL; + return (objc_protocolopt_t *)((uint8_t *)this + protocolopt_offset); + } }; // sizeof(objc_opt_t) must be pointer-aligned @@ -602,9 +688,18 @@ STATIC_ASSERT(sizeof(objc_opt_t) % sizeof(void*) == 0); 4, 4, 63, 3, 0, 0, 0,0, X256(0), 0, 0, 16, 16, 16, 16 \ /* no objc_headeropt_t */ \ /* no objc_clsopt_t */ \ + /* no objc_protocolopt_t */ \ } +// List of offsets in libobjc that the shared cache optimization needs to use. +template +struct objc_opt_pointerlist_tt { + T protocolClass; +}; +typedef struct objc_opt_pointerlist_tt objc_opt_pointerlist_t; + + /* -------------------------------------------------------------------- mix -- mix 3 64-bit values reversibly. diff --git a/launch-cache/CacheFileAbstraction.hpp b/launch-cache/CacheFileAbstraction.hpp index cd2524e..c6aca25 100644 --- a/launch-cache/CacheFileAbstraction.hpp +++ b/launch-cache/CacheFileAbstraction.hpp @@ -70,6 +70,9 @@ public: const uint8_t* uuid() const INLINE { return fields.uuid; } void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } + + uint64_t cacheType() const INLINE { return E::get64(fields.cacheType); } + void set_cacheType(uint64_t value) INLINE { E::set64(fields.cacheType, value); } private: dyld_cache_header fields; diff --git a/launch-cache/MachOBinder.hpp b/launch-cache/MachOBinder.hpp index 2b8a3ac..d87fe23 100644 --- a/launch-cache/MachOBinder.hpp +++ b/launch-cache/MachOBinder.hpp @@ -77,7 +77,7 @@ public: typedef std::unordered_map*, CStringHash, CStringEquals> Map; - Binder(const MachOLayoutAbstraction&, uint64_t dyldBaseAddress); + Binder(const MachOLayoutAbstraction&); virtual ~Binder() {} const char* getDylibID() const; @@ -93,14 +93,10 @@ private: struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; }; typedef std::unordered_map NameToAddrMap; typedef std::unordered_set NameSet; - struct ClientAndSymbol { Binder* client; const char* symbolName; }; - struct SymbolAndLazyPointer { const char* symbolName; pint_t lpVMAddr; }; + typedef std::unordered_map*>, CStringHash, CStringEquals> ResolverClientsMap; + static bool isPublicLocation(const char* pth); - void doBindExternalRelocations(); - void doBindIndirectSymbols(); - void doSetUpDyldSection(); - void doSetPreboundUndefines(); void hoistPrivateRexports(); int ordinalOfDependentBinder(Binder* dep); void doBindDyldInfo(std::vector& pointersInData); @@ -109,15 +105,14 @@ private: int libraryOrdinal, int64_t addend, const char* symbolName, bool lazyPointer, bool weakImport, std::vector& pointersInData); - pint_t resolveUndefined(const macho_nlist

* undefinedSymbol); bool findExportedSymbolAddress(const char* name, pint_t* result, Binder** 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

* sym); - void optimizeStub(const char* symbolName, pint_t lpVMAddr); - void optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr); + void switchStubToUseSharedLazyPointer(const char* symbolName, pint_t lpVMAddr); + void switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr); pint_t findLazyPointerFor(const char* symbolName); + void shareLazyPointersToResolvers(); static uint8_t pointerRelocSize(); @@ -128,7 +123,6 @@ private: NameSet fSymbolResolvers; NameSet fAbsoluteSymbols; std::vector fReExportedSymbols; - uint64_t fDyldBaseAddress; const macho_nlist

* fSymbolTable; const char* fStrings; const macho_dysymtab_command

* fDynamicInfo; @@ -138,8 +132,7 @@ private: const macho_dyld_info_command

* fDyldInfo; bool fOriginallyPrebound; bool fReExportedSymbolsResolved; - std::vector fClientAndSymbols; - NameToAddrMap fResolverLazyPointers; + ResolverClientsMap fResolverInfo; }; template <> @@ -159,8 +152,8 @@ typename A::P::uint_t Binder::runtimeAddressFromNList(const macho_nlist

* s template -Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress) - : Rebaser(layout), fDyldBaseAddress(dyldBaseAddress), +Binder::Binder(const MachOLayoutAbstraction& layout) + : Rebaser(layout), fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL), fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL), fParentUmbrella(NULL), fReExportedSymbolsResolved(false) @@ -202,7 +195,7 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress fDyldInfo = (macho_dyld_info_command

*)cmd; break; case LC_RPATH: - throwf("dyld shared cache does not support LC_RPATH found in %s", layout.getFilePath()); + fprintf(stderr, "update_dyld_shared_cache: warning: dyld shared cache does not support LC_RPATH found in %s\n", layout.getFilePath()); break; default: if ( cmd->cmd() & LC_REQ_DYLD ) @@ -214,72 +207,48 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress throw "no LC_DYSYMTAB"; if ( fSymbolTable == NULL ) throw "no LC_SYMTAB"; + if ( fDyldInfo == NULL ) + throw "no LC_DYLD_INFO"; // build hash table -// fprintf(stderr, "exports for %s\n", layout.getFilePath()); - if ( fDyldInfo != NULL ) { - std::vector 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::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 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::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

* start = &fSymbolTable[fDynamicInfo->iextdefsym()]; - const macho_nlist

* end = &start[fDynamicInfo->nextdefsym()]; - fHashTable.reserve(fDynamicInfo->nextdefsym()); // set initial bucket count - for (const macho_nlist

* 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

* sym = &fSymbolTable[index]; - const char* name = &fStrings[sym->n_strx()]; - fHashTable[name] = runtimeAddressFromNList(sym); - //fprintf(stderr, "- 0x%08llX %s\n", sym->n_value(), name); - } + } + break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + fHashTable[it->name] = it->address + baseAddress; + break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + fHashTable[it->name] = it->address; + fAbsoluteSymbols.insert(it->name); + break; + default: + throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath()); + break; } + //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name); } } @@ -341,7 +310,7 @@ bool Binder::isPublicLocation(const char* pth) template void Binder::setDependentBinders(const Map& map) { - // first pass to build vector of dylibs + // build vector of dependent dylibs const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); const uint32_t cmd_count = this->fHeader->ncmds(); const macho_load_command

* cmd = cmds; @@ -400,64 +369,7 @@ void Binder::setDependentBinders(const Map& map) break; } cmd = (const macho_load_command

*)(((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

*)cmd)->sub_library(); - for (typename std::vector::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

*)cmd)->sub_umbrella(); - for (typename std::vector::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

*)(((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::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { - if ( ! it->reExport ) { - Binder* dep = it->binder; - if ( dep != NULL ) { - const char* parentUmbrellaName = dep->parentUmbrella(); - if ( parentUmbrellaName != NULL ) { - if ( strcmp(parentUmbrellaName, thisLeafName) == 0 ) - it->reExport = true; - } - } - } - } - } - } - + } } template @@ -523,47 +435,9 @@ void Binder::hoistPrivateRexports() template void Binder::bind(std::vector& 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 -void Binder::doSetUpDyldSection() -{ - // find __DATA __dyld section - const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); - const uint32_t cmd_count = this->fHeader->ncmds(); - const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == macho_segment_command

::CMD ) { - const macho_segment_command

* seg = (macho_segment_command

*)cmd; - if ( strcmp(seg->segname(), "__DATA") == 0 ) { - const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)seg + sizeof(macho_segment_command

)); - const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; - for (const macho_section

* 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

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } + this->doBindDyldInfo(pointersInData); + this->doBindDyldLazyInfo(pointersInData); + // weak bind info is processed at launch time } template @@ -793,232 +667,6 @@ void Binder::doBindDyldInfo(std::vector& pointersInData) default: throwf("bad bind opcode %d", *p); } - } - - - -} - - -template -void Binder::doSetPreboundUndefines() -{ - const macho_dysymtab_command

* dysymtab = NULL; - macho_nlist

* symbolTable = NULL; - - // get symbol table info - const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); - const uint32_t cmd_count = this->fHeader->ncmds(); - const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SYMTAB: - { - const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; - symbolTable = (macho_nlist

*)(&this->fLinkEditBase[symtab->symoff()]); - } - break; - case LC_DYSYMTAB: - dysymtab = (macho_dysymtab_command

*)cmd; - break; - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } - - // walk all undefines and set their prebound n_value - macho_nlist

* const lastUndefine = &symbolTable[dysymtab->iundefsym()+dysymtab->nundefsym()]; - for (macho_nlist

* 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 -void Binder::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& segments = this->fLayout.getSegments(); - for(std::vector::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

* const relocsStart = (macho_relocation_info

*)(&this->fLinkEditBase[fDynamicInfo->extreloff()]); - const macho_relocation_info

* const relocsEnd = &relocsStart[fDynamicInfo->nextrel()]; - for (const macho_relocation_info

* 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

* 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 -void Binder::bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value) -{ - // do nothing -} - -// x86 supports fast stubs -template <> -void Binder::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 -void Binder::doBindIndirectSymbols() -{ - const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()]; - const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); - const uint32_t cmd_count = this->fHeader->ncmds(); - const macho_load_command

* 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

::CMD ) { - const macho_segment_command

* seg = (macho_segment_command

*)cmd; - const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)seg + sizeof(macho_segment_command

)); - const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; - for (const macho_section

* 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

* 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

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } -} - - - - -template -typename A::P::uint_t Binder::resolveUndefined(const macho_nlist

* 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* 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* foundIn; - if ( ! binder->findExportedSymbolAddress(symbolName, &addr, &foundIn, &isResolver, &isAbsolute) ) - throwf("could not resolve undefined symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID()); - return addr; } } @@ -1084,10 +732,7 @@ bool Binder::findExportedSymbolAddress(const char* name, pint_t* result, Bind template void Binder::addResolverClient(Binder* clientDylib, const char* symbolName) { - ClientAndSymbol x; - x.client = clientDylib; - x.symbolName = symbolName; - fClientAndSymbols.push_back(x); + fResolverInfo[symbolName].insert(clientDylib); } @@ -1095,15 +740,8 @@ template typename A::P::uint_t Binder::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

* const cmds = (macho_load_command

*)((uint8_t*)this->fHeader + sizeof(macho_header

)); const uint32_t cmd_count = this->fHeader->ncmds(); @@ -1130,8 +768,7 @@ typename A::P::uint_t Binder::findLazyPointerFor(const char* symbolName) const char* aName = &fStrings[aSymbol->n_strx()]; //fprintf(stderr, " sect=%s, index=%d, symbolIndex=%d, sym=%s\n", sect->sectname(), j, symbolIndex, &fStrings[undefinedSymbol->n_strx()]); if ( strcmp(aName, symbolName) == 0 ) { - fResolverLazyPointers[symbolName] = vmlocation; - if ( log ) fprintf(stderr, "found slow-path shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID()); + if ( log ) fprintf(stderr, "found shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID()); return vmlocation; } break; @@ -1172,20 +809,30 @@ typename A::P::uint_t Binder::findLazyPointerFor(const char* symbolName) template void Binder::optimize() { - for (typename std::vector::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 +void Binder::shareLazyPointersToResolvers() +{ + for (const auto &entry : fResolverInfo) { + const char* resolverSymbolName = entry.first; + if ( pint_t lpVMAddr = findLazyPointerFor(resolverSymbolName) ) { + for (Binder* 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::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) +void Binder::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) { if ( stubSize != 16 ) { fprintf(stderr, "could not optimize ARM stub to resolver function in %s because it is wrong size\n", this->getDylibID()); @@ -1207,7 +854,7 @@ void Binder::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, template <> -void Binder::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) +void Binder::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr) { if ( stubSize != 6 ) { fprintf(stderr, "could not optimize x86_64 stub to resolver function in %s because it is wrong size\n", this->getDylibID()); @@ -1224,7 +871,7 @@ void Binder::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddre } template -void Binder::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress) +void Binder::switchStubsLazyPointer(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress) { // Remaining architectures are not optimized //fprintf(stderr, "optimize stub at %p in %s to use lazyPointer at 0x%llX\n", stubMappedAddress, this->getDylibID(), (uint64_t)lpVMAddress); @@ -1232,7 +879,7 @@ void Binder::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, u // search for stub in this image that call target symbol name and then optimize its lazy pointer template -void Binder::optimizeStub(const char* stubName, pint_t lpVMAddr) +void Binder::switchStubToUseSharedLazyPointer(const char* stubName, pint_t lpVMAddr) { // find named stub const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()]; @@ -1264,7 +911,7 @@ void Binder::optimizeStub(const char* stubName, pint_t lpVMAddr) const macho_nlist

* 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; } diff --git a/launch-cache/MachOFileAbstraction.hpp b/launch-cache/MachOFileAbstraction.hpp index 7a65370..4cda3d3 100644 --- a/launch-cache/MachOFileAbstraction.hpp +++ b/launch-cache/MachOFileAbstraction.hpp @@ -96,7 +96,23 @@ struct uuid_command { #ifndef CPU_SUBTYPE_X86_64_H #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) -#endif +#endif + + +#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F + +#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03 +#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04 +#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05 +#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06 +#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07 +#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08 +#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09 +#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A +#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B +#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C #include "FileAbstraction.hpp" @@ -801,7 +817,7 @@ public: const macho_segment_command

* segcmd = (macho_segment_command

*)cmd; if (0 == strncmp(segname, segcmd->segname(), 16)) { return segcmd; - } + } } cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -821,6 +837,8 @@ public: } } + if (strcmp(segname, "__DATA") == 0) + return getSection("__DATA_CONST", sectname); return NULL; } diff --git a/launch-cache/MachOLayout.hpp b/launch-cache/MachOLayout.hpp index 4d90a1e..c799e30 100644 --- a/launch-cache/MachOLayout.hpp +++ b/launch-cache/MachOLayout.hpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -69,11 +70,13 @@ public: struct Segment { public: - Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t align, - uint32_t prot, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize), + Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t sectionsSize, + uint64_t sectionsAlignment, uint64_t align, + uint32_t prot, uint32_t sectionCount, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize), fOrigFileOffset(offset), fOrigFileSize(file_size), fOrigPermissions(prot), fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align), - fPermissions(prot), fNewAddress(0), fMappedAddress(NULL) { + fPermissions(prot), fSectionCount(sectionCount), fSectionsSize(sectionsSize), + fSectionsAlignment(sectionsAlignment), fNewAddress(0), fMappedAddress(NULL) { strlcpy(fOrigName, segName, 16); } @@ -88,6 +91,9 @@ public: uint64_t alignment() const { return fAlignment; } const char* name() const { return fOrigName; } uint64_t newAddress() const { return fNewAddress; } + uint32_t sectionCount() const{ return fSectionCount; } + uint64_t sectionsSize() const{ return fSectionsSize; } + uint64_t sectionsAlignment() const { return fSectionsAlignment; } void* mappedAddress() const { return fMappedAddress; } void setNewAddress(uint64_t addr) { fNewAddress = addr; } void setMappedAddress(void* addr) { fMappedAddress = addr; } @@ -95,6 +101,7 @@ public: void setFileOffset(uint64_t new_off) { fFileOffset = new_off; } void setFileSize(uint64_t new_size) { fFileSize = new_size; } void setWritable(bool w) { if (w) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; } + void setSectionsAlignment(uint64_t v){ fSectionsAlignment = v; } void reset() { fSize=fOrigSize; fFileOffset=fOrigFileOffset; fFileSize=fOrigFileSize; fPermissions=fOrigPermissions; } private: uint64_t fOrigAddress; @@ -108,6 +115,9 @@ public: uint64_t fFileSize; uint64_t fAlignment; uint32_t fPermissions; + uint32_t fSectionCount; + uint64_t fSectionsSize; + uint64_t fSectionsAlignment; uint64_t fNewAddress; void* fMappedAddress; }; @@ -130,7 +140,8 @@ public: virtual bool isDylib() const = 0; virtual bool isSplitSeg() const = 0; virtual bool hasSplitSegInfo() const = 0; - virtual bool isRootOwned() const = 0; + virtual bool hasSplitSegInfoV2() const = 0; + virtual int notTrusted() const = 0; virtual bool inSharableLocation() const = 0; virtual bool hasDynamicLookupLinkage() const = 0; virtual bool hasMainExecutableLookupLinkage() const = 0; @@ -177,8 +188,9 @@ public: virtual Library getID() const { return fDylibID; } virtual bool isDylib() const { return fIsDylib; } virtual bool isSplitSeg() const; - virtual bool hasSplitSegInfo() const { return fHasSplitSegInfo; } - virtual bool isRootOwned() const { return fRootOwned; } + virtual bool hasSplitSegInfo() const { return fSplitSegInfo != NULL; } + virtual bool hasSplitSegInfoV2() const{ return fHasSplitSegInfoV2; } + virtual int notTrusted() const { return fRootlessErrno; } virtual bool inSharableLocation() const { return fShareableLocation; } virtual bool hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; } virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; } @@ -212,6 +224,9 @@ private: uint64_t segmentSize(const macho_segment_command* segCmd) const; uint64_t segmentFileSize(const macho_segment_command* segCmd) const; uint64_t segmentAlignment(const macho_segment_command* segCmd) const; + uint64_t sectionsSize(const macho_segment_command* segCmd) const; + uint64_t sectionsAlignment(const macho_segment_command* segCmd) const; + bool validReadWriteSeg(const Segment& seg) const; static cpu_type_t arch(); @@ -235,8 +250,9 @@ private: uint64_t fVMExecutableSize; uint64_t fVMWritablSize; uint64_t fVMReadOnlySize; - bool fHasSplitSegInfo; - bool fRootOwned; + const macho_linkedit_data_command

* fSplitSegInfo; + bool fHasSplitSegInfoV2; + int fRootlessErrno; bool fShareableLocation; bool fDynamicLookupLinkage; bool fMainExecutableLookupLinkage; @@ -506,10 +522,41 @@ uint64_t MachOLayout::segmentFileSize(const macho_segment_commandfilesize(); } +template +uint64_t MachOLayout::sectionsSize(const macho_segment_command* segCmd) const +{ + if ( segCmd->nsects() > 0 ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* 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 +uint64_t MachOLayout::sectionsAlignment(const macho_segment_command* segCmd) const +{ + int p2align = 4; + if ( hasSplitSegInfoV2() && (segCmd->nsects() > 0) ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)segCmd + sizeof(macho_segment_command

)); + const macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()-1]; + for (const macho_section

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->align() > p2align ) + p2align = sect->align(); + } + } + return (1 << p2align); +} + + template MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid) - : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0), + : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fSplitSegInfo(NULL), fHasSplitSegInfoV2(false), fRootlessErrno(0), fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false), fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL) { @@ -537,7 +584,9 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* fFileType = mh->filetype(); fArchPair.arch = mh->cputype(); fArchPair.subtype = mh->cpusubtype(); - + if ( rootless_check_trusted(path) != 0 && rootless_protected_volume(path) == 1) + fRootlessErrno = errno; + const macho_dyld_info_command

* dyldInfo = NULL; const macho_symtab_command

* symbolTableCmd = NULL; const macho_dysymtab_command

* dynamicSymbolTableCmd = NULL; @@ -571,13 +620,15 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* } break; case LC_SEGMENT_SPLIT_INFO: - fHasSplitSegInfo = true; + fSplitSegInfo = (macho_linkedit_data_command

*)cmd; break; case macho_segment_command

::CMD: { const macho_segment_command

* segCmd = (macho_segment_command

*)cmd; fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(), - segmentFileSize(segCmd), segmentAlignment(segCmd), segCmd->initprot(), segCmd->segname())); + segmentFileSize(segCmd), sectionsSize(segCmd), sectionsAlignment(segCmd), + segmentAlignment(segCmd), segCmd->initprot(), + segCmd->nsects(), segCmd->segname())); } break; case LC_SYMTAB: @@ -658,6 +709,18 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* } } + if ( fSplitSegInfo != NULL ) { + const uint8_t* infoStart = (uint8_t*)machHeader + fSplitSegInfo->dataoff(); + fHasSplitSegInfoV2 = ( *infoStart == DYLD_CACHE_ADJ_V2_FORMAT ); + if ( !fHasSplitSegInfoV2 ) { + // split seg version not known when segments created + // v1 does not support packing, so simulate that by forcing alignment + for (Segment& seg : fSegments) { + seg.setSectionsAlignment(4096); + } + } + } + } template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_I386; } diff --git a/launch-cache/MachORebaser.hpp b/launch-cache/MachORebaser.hpp index ac80cd0..21a4634 100644 --- a/launch-cache/MachORebaser.hpp +++ b/launch-cache/MachORebaser.hpp @@ -56,7 +56,7 @@ public: virtual cpu_type_t getArchitecture() const = 0; virtual uint64_t getBaseAddress() const = 0; virtual uint64_t getVMSize() const = 0; - virtual void rebase(std::vector&) = 0; + virtual bool rebase(std::vector&) = 0; }; @@ -70,7 +70,7 @@ public: virtual cpu_type_t getArchitecture() const; virtual uint64_t getBaseAddress() const; virtual uint64_t getVMSize() const; - virtual void rebase(std::vector&); + virtual bool rebase(std::vector&); protected: typedef typename A::P P; @@ -79,24 +79,21 @@ protected: pint_t* mappedAddressForNewAddress(pint_t vmaddress); pint_t getSlideForNewAddress(pint_t newAddress); - + private: - void calculateRelocBase(); void adjustLoadCommands(); void adjustSymbolTable(); - void optimzeStubs(); - void makeNoPicStub(uint8_t* stub, pint_t logicalAddress); void adjustDATA(); void adjustCode(); void applyRebaseInfo(std::vector& pointersInData); - void adjustExportInfo(); + void adjustReferencesUsingInfoV2(std::vector& 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& pointersInData); + bool adjustExportInfo(); void doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersInData); - void adjustSegmentLoadCommand(macho_segment_command

* 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

* reloc); @@ -107,23 +104,20 @@ protected: uint8_t* fLinkEditBase; // add file offset to this to get linkedit content const MachOLayoutAbstraction& fLayout; private: - pint_t fOrignalVMRelocBaseAddress; // add reloc address to this to get original address reloc referred to const macho_symtab_command

* fSymbolTable; const macho_dysymtab_command

* fDynamicSymbolTable; const macho_dyld_info_command

* fDyldInfo; + const macho_linkedit_data_command

* fSplitSegInfo; bool fSplittingSegments; - bool fOrignalVMRelocBaseAddressValid; - pint_t fSkipSplitSegInfoStart; - pint_t fSkipSplitSegInfoEnd; + bool fHasSplitSegInfoV2; + std::vector fSectionOffsetsInSegment; }; - template Rebaser::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

*)fLayout.getSegments()[0].mappedAddress(); switch ( fHeader->filetype() ) { @@ -161,13 +155,31 @@ Rebaser::Rebaser(const MachOLayoutAbstraction& layout) case LC_DYLD_INFO_ONLY: fDyldInfo = (macho_dyld_info_command

*)cmd; break; + case LC_SEGMENT_SPLIT_INFO: + fSplitSegInfo = (macho_linkedit_data_command

*)cmd; + break; + case macho_segment_command

::CMD: { + // update segment/section file offsets + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[segCmd->nsects()]; + for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + fSectionOffsetsInSegment.push_back(sect->addr() - segCmd->vmaddr()); + } + } } cmd = (const macho_load_command

*)(((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::getArchitecture() const { return CPU_TYPE_I386; } @@ -210,48 +222,30 @@ uint64_t Rebaser::getVMSize() const template -void Rebaser::rebase(std::vector& pointersInData) -{ - // update writable segments that have internal pointers - if ( fDyldInfo != NULL ) +bool Rebaser::rebase(std::vector& 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::adjustSegmentLoadCommand(macho_segment_command

* 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 -void Rebaser::adjustSegmentLoadCommand(macho_segment_command

* seg) -{ + // update export info + return this->adjustExportInfo(); } - template void Rebaser::adjustLoadCommands() { @@ -288,7 +282,6 @@ void Rebaser::adjustLoadCommands() // update segment commands { macho_segment_command

* seg = (macho_segment_command

*)cmd; - this->adjustSegmentLoadCommand(seg); pint_t slide = this->getSlideForVMAddress(seg->vmaddr()); seg->set_vmaddr(seg->vmaddr() + slide); macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); @@ -371,76 +364,6 @@ typename A::P::uint_t Rebaser::getSlideForNewAddress(pint_t newAddress) throwf("new address 0x%08llX not found", (uint64_t)newAddress); } -template -typename A::P::uint_t* Rebaser::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::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::optimzeStubs() -{ - // convert pic stubs to no-pic stubs in dyld shared cache - const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); - const uint32_t cmd_count = fHeader->ncmds(); - const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == macho_segment_command

::CMD ) { - macho_segment_command

* seg = (macho_segment_command

*)cmd; - macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); - macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; - for(macho_section

* 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

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } -} -#endif - -template -void Rebaser::optimzeStubs() -{ - // other architectures don't need stubs changed in shared cache -} - template void Rebaser::adjustSymbolTable() { @@ -462,15 +385,15 @@ void Rebaser::adjustSymbolTable() } template -void Rebaser::adjustExportInfo() +bool Rebaser::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 originalExports; try { @@ -507,13 +430,24 @@ void Rebaser::adjustExportInfo() while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 ) newExportTrieBytes.push_back(0); - // allocate new buffer and set export_off to use new buffer instead uint32_t newExportsSize = newExportTrieBytes.size(); - uint8_t* sideTrie = new uint8_t[newExportsSize]; - memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize); - fLayout.setDyldInfoExports(sideTrie); - ((macho_dyld_info_command

*)fDyldInfo)->set_export_off(0); // invalidate old trie - ((macho_dyld_info_command

*)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

*)fDyldInfo)->set_export_off(0); // invalidate old trie + ((macho_dyld_info_command

*)fDyldInfo)->set_export_size(newExportsSize); + return false; + } } @@ -521,15 +455,6 @@ void Rebaser::adjustExportInfo() template void Rebaser::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta) { - // begin hack for 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 - //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX, path=%s)\n", // kind, address, codeToDataDelta, codeToImportDelta, fLayout.getFilePath()); uint32_t* p; @@ -692,39 +617,8 @@ void Rebaser::adjustCode() { if ( fSplittingSegments ) { // get uleb128 compressed runs of code addresses to update - const uint8_t* infoStart = NULL; - const uint8_t* infoEnd = NULL; - const macho_segment_command

* seg; - const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); - const uint32_t cmd_count = fHeader->ncmds(); - const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd()) { - case LC_SEGMENT_SPLIT_INFO: - { - const macho_linkedit_data_command

* segInfo = (macho_linkedit_data_command

*)cmd; - infoStart = &fLinkEditBase[segInfo->dataoff()]; - infoEnd = &infoStart[segInfo->datasize()]; - } - break; - // begin hack for split seg info wrong for x86_64 stub helpers - case macho_segment_command

::CMD: - seg = (macho_segment_command

*)cmd; - if ( (getArchitecture() == CPU_TYPE_X86_64) && (strcmp(seg->segname(), "__TEXT") == 0) ) { - const macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); - const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; - for(const macho_section

* 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 split seg info wrong for x86_64 stub helpers - } - cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } + const uint8_t* infoStart = &fLinkEditBase[fSplitSegInfo->dataoff()]; + const uint8_t* infoEnd = &infoStart[fSplitSegInfo->datasize()];; // calculate how much we need to slide writable segments const uint64_t orgBaseAddress = this->getBaseAddress(); int64_t codeToDataDelta = 0; @@ -733,9 +627,7 @@ void Rebaser::adjustCode() const MachOLayoutAbstraction::Segment& codeSeg = segments[0]; for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { const MachOLayoutAbstraction::Segment& dataSeg = *it; - if ( strcmp(dataSeg.name(), "__IMPORT") == 0 ) - codeToImportDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); - else if ( dataSeg.writable() ) { + if ( dataSeg.writable() ) { if ( (strcmp(dataSeg.name(), "__DATA") != 0) && (strcmp(dataSeg.name(), "__OBJC") != 0) ) throwf("only one rw segment named '__DATA' can be used in dylibs placed in the dyld shared cache (%s)", fLayout.getFilePath()); codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); @@ -860,262 +752,368 @@ void Rebaser::applyRebaseInfo(std::vector& pointersInData) } } -template -void Rebaser::adjustDATA() +template <> +void Rebaser::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& pointersInData) { - // walk all local relocations and slide every pointer - const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(&fLinkEditBase[fDynamicSymbolTable->locreloff()]); - const macho_relocation_info

* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()]; - for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { - this->doLocalRelocation(reloc); - } - - // walk non-lazy-pointers and slide the ones that are LOCAL - const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); - const uint32_t cmd_count = fHeader->ncmds(); - const macho_load_command

* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == macho_segment_command

::CMD ) { - const macho_segment_command

* seg = (macho_segment_command

*)cmd; - const macho_section

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); - const macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; - const uint32_t* const indirectTable = (uint32_t*)(&fLinkEditBase[fDynamicSymbolTable->indirectsymoff()]); - for(const macho_section

* 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

*)(((uint8_t*)cmd)+cmd->cmdsize()); - } -} - - -template -void Rebaser::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& segments = fLayout.getSegments(); - for(std::vector::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

* const relocsStart = (macho_relocation_info

*)(&fLinkEditBase[fDynamicSymbolTable->locreloff()]); - macho_relocation_info

* const relocsEnd = &relocsStart[fDynamicSymbolTable->nlocrel()]; - for (macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { - reloc->set_r_address(reloc->r_address()-relocAddressAdjust); - } - - // walk all external relocations and adjust every address - macho_relocation_info

* const externRelocsStart = (macho_relocation_info

*)(&fLinkEditBase[fDynamicSymbolTable->extreloff()]); - macho_relocation_info

* const externRelocsEnd = &externRelocsStart[fDynamicSymbolTable->nextrel()]; - for (macho_relocation_info

* 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::adjustRelocBaseAddresses() +static bool isThumbMovw(uint32_t instruction) { - // x86_64 already have reloc base of first writable segment + return ( (instruction & 0x8000FBF0) == 0x0000F240 ); } - -template <> -void Rebaser::doLocalRelocation(const macho_relocation_info* 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::doLocalRelocation(const macho_relocation_info

* 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

* sreloc = (macho_scattered_relocation_info

*)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 -void Rebaser::doLocalRelocation(const macho_relocation_info

* 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 -void Rebaser::calculateRelocBase() +static bool isArmMovw(uint32_t instruction) { - const std::vector& segments = fLayout.getSegments(); - if ( fHeader->flags() & MH_SPLIT_SEGS ) { - // reloc addresses are from the start of the first writable segment - for(std::vector::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::calculateRelocBase() +static uint16_t getArmWord(uint32_t instruction) { - // reloc addresses are always based from the start of the first writable segment - const std::vector& segments = fLayout.getSegments(); - for(std::vector::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::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& 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(&p[fileOffset])); - break; - case CPU_TYPE_X86_64: - fRebasers.push_back(new Rebaser(&p[fileOffset])); - break; - case CPU_TYPE_ARM: - fRebasers.push_back(new Rebaser(&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(mh)); - } - else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) { - fRebasers.push_back(new Rebaser(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(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 +void Rebaser::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& pointersInData) +{ + throw "v2 split seg info not supported yet"; +} - const std::vector& getArchs() const { return fRebasers; } - void commit() { ::msync(fMappingAddress, fFileSize, MS_ASYNC); } -private: - std::vector fRebasers; - void* fMappingAddress; - uint64_t fFileSize; -}; -#endif +template +void Rebaser::adjustReferencesUsingInfoV2(std::vector& 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 sectionSlides; + std::vector sectionNewAddress; + std::vector 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 :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + 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__ diff --git a/launch-cache/ObjCLegacyAbstraction.hpp b/launch-cache/ObjCLegacyAbstraction.hpp index 19ee2d3..23018c7 100644 --- a/launch-cache/ObjCLegacyAbstraction.hpp +++ b/launch-cache/ObjCLegacyAbstraction.hpp @@ -151,6 +151,7 @@ template class LegacySelectorUpdater { typedef typename A::P P; + typedef typename A::P::uint_t pint_t; static void visitMethodList(objc_method_list *mlist, V& visitor) { @@ -224,10 +225,10 @@ public: // Message refs PointerSection 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); } } }; diff --git a/launch-cache/ObjCModernAbstraction.hpp b/launch-cache/ObjCModernAbstraction.hpp index e41749d..dfa332a 100644 --- a/launch-cache/ObjCModernAbstraction.hpp +++ b/launch-cache/ObjCModernAbstraction.hpp @@ -96,17 +96,6 @@ struct entsize_iterator { bool operator > (const entsize_iterator& rhs) { return this->current > rhs.current; } - - - static void overwrite(entsize_iterator& dst, const Tlist* srcList) - { - entsize_iterator src; - uint32_t ee = srcList->getEntsize(); - for (src = srcList->begin(); src != srcList->end(); ++src) { - memcpy(&*dst, &*src, ee); - ++dst; - } - } }; template @@ -426,18 +415,41 @@ private: void* operator new (size_t); }; + +template class objc_protocol_list_t; // forward reference + template 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* 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 *getProtocols(SharedCache* cache) const { return (objc_protocol_list_t *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); } + objc_method_list_t *getInstanceMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); } objc_method_list_t *getClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); } @@ -446,6 +458,42 @@ public: objc_method_list_t *getOptionalClassMethods(SharedCache* cache) const { return (objc_method_list_t *)cache->mappedAddressForVMAddress(A::P::getP(optionalClassMethods)); } + objc_property_list_t *getInstanceProperties(SharedCache* cache) const { return (objc_property_list_t *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); } + + pint_t *getExtendedMethodTypes(SharedCache* cache) const { + if (getSize() < offsetof(objc_protocol_t, extendedMethodTypes) + sizeof(extendedMethodTypes)) { + return NULL; + } + return (pint_t *)cache->mappedAddressForVMAddress(A::P::getP(extendedMethodTypes)); + } + + const char *getDemangledName(SharedCache* cache) const { + if (sizeof(*this) < offsetof(objc_protocol_t, demangledName) + sizeof(demangledName)) { + return NULL; + } + return (const char *)cache->mappedAddressForVMAddress(A::P::getP(demangledName)); + } + + void setDemangledName(SharedCache* cache, const char *newName) { + if (sizeof(*this) < offsetof(objc_protocol_t, demangledName) + sizeof(demangledName)) { + throw "objc protocol has the wrong size"; + } + A::P::setP(demangledName, cache->VMAddressForMappedAddress(newName)); + } + + void addPointers(std::vector& 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 @@ -460,14 +508,20 @@ public: pint_t getCount() const { return A::P::getP(count); } + pint_t getVMAddress(pint_t i) { + return A::P::getP(list[i]); + } + objc_protocol_t* get(SharedCache* cache, pint_t i) { - return (objc_protocol_t*)cache->mappedAddressForVMAddress(A::P::getP(list[i])); + return (objc_protocol_t*)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* src) { - pint_t srcCount = src->getCount(); - memcpy(list+index, src->list, srcCount * sizeof(pint_t)); - index += srcCount; + void set(SharedCache* cache, pint_t i, objc_protocol_t* proto) { + setVMAddress(i, cache->VMAddressForMappedAddress(proto)); } uint32_t byteSize() const { @@ -719,13 +773,97 @@ public: for (pint_t i = 0; i < classes.count(); i++) { objc_class_t *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 +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* cache, const macho_header

* header) + { + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t *proto = protocols.get(i); + protocolVisitor.visitProtocol(cache, header, proto); + } + } +}; + + +// Call visitor.visitProtocolReference() on every protocol. +template +class ProtocolReferenceWalker { + typedef typename A::P P; + typedef typename A::P::uint_t pint_t; + V& mVisitor; + + void visitProtocolList(SharedCache* cache, + objc_protocol_list_t* 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>; + void visitClass(SharedCache* cache, const macho_header

*, + objc_class_t* cls) + { + visitProtocolList(cache, cls->getProtocolList(cache)); + visitProtocolList(cache, cls->getIsa(cache)->getProtocolList(cache)); + } + +public: + + ProtocolReferenceWalker(V& visitor) : mVisitor(visitor) { } + void walk(SharedCache* cache, const macho_header

* header) + { + // @protocol expressions + PointerSection *> + 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> classes(*this); + classes.walk(cache, header); + + // protocol lists in protocols + // __objc_protolists itself is NOT updated + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t* 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 class MethodListWalker { @@ -738,7 +876,7 @@ public: MethodListWalker(V& visitor) : mVisitor(visitor) { } - void walk(SharedCache* cache, const macho_header

* header, bool walkProtocols) + void walk(SharedCache* cache, const macho_header

* header) { // Method lists in classes PointerSection *> @@ -769,27 +907,31 @@ public: } } - // Method description lists from protocols - if ( walkProtocols ) { - PointerSection *> - protocols(cache, header, "__DATA", "__objc_protolist"); - for (pint_t i = 0; i < protocols.count(); i++) { - objc_protocol_t *proto = protocols.get(i); - objc_method_list_t *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 *> + protocols(cache, header, "__DATA", "__objc_protolist"); + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t *proto = protocols.get(i); + objc_method_list_t *mlist; + pint_t *typelist = proto->getExtendedMethodTypes(cache); + + if ((mlist = proto->getInstanceMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getClassMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getOptionalInstanceMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getOptionalClassMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + } } }; @@ -815,23 +957,28 @@ class SelectorOptimizer { // Do not setFixedUp: the methods are not yet sorted. } + void visitProtocolMethodList(objc_method_list_t *mlist, pint_t *types) + { + visitMethodList(mlist); + } + public: SelectorOptimizer(V& visitor) : mVisitor(visitor) { } void optimize(SharedCache* cache, const macho_header

* header) { - // method lists of all kinds + // method lists in classes, categories, and protocols MethodListWalker< A, SelectorOptimizer > mw(*this); - mw.walk(cache, header, true); + mw.walk(cache, header); // @selector references PointerSection selrefs(cache, header, "__DATA", "__objc_selrefs"); for (pint_t i = 0; i < selrefs.count(); i++) { - pint_t oldValue = selrefs.getUnmapped(i); + pint_t oldValue = selrefs.getVMAddress(i); pint_t newValue = mVisitor.visit(oldValue); - selrefs.set(i, newValue); + selrefs.setVMAddress(i, newValue); } // message references @@ -847,6 +994,41 @@ public: }; +template +static bool headerSupportsGC(SharedCache* cache, + const macho_header* header) +{ + const macho_section *imageInfoSection = + header->getSection("__DATA", "__objc_imageinfo"); + if (imageInfoSection) { + objc_image_info *info = (objc_image_info *) + cache->mappedAddressForVMAddress(imageInfoSection->addr()); + return (info->supportsGCFlagSet() || info->requiresGCFlagSet()); + } + + return false; +} + + +// Gather the set of GC-supporting classes +template +class GCClassSet { + typedef typename A::P P; + + std::set*> fGCClasses; + +public: + bool contains(objc_class_t* cls) const { + return fGCClasses.count(cls) != 0; + } + + void visitClass(SharedCache* cache, const macho_header

* header, objc_class_t *cls) + { + fGCClasses.insert(cls); + } +}; + + // Update selector references. The visitor performs recording and uniquing. template class IvarOffsetOptimizer { @@ -857,6 +1039,8 @@ class IvarOffsetOptimizer { uint32_t fOptimized; + GCClassSet fGCClasses; + public: IvarOffsetOptimizer() : fOptimized(0) { } @@ -887,6 +1071,12 @@ public: // The slide algorithm is also implemented in objc. Any changes here should be reflected there also. void visitClass(SharedCache* cache, const macho_header

* /*unused, may be NULL*/, objc_class_t *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 *super = cls->getSuperclass(cache); if (super) { // Recursively visit superclasses to ensure we have the correct superclass start @@ -919,20 +1109,24 @@ public: } } } - + + // Gather the list of GC-supporting classes. + // Ivars in these classes cannot be updated because + // we don't know how to update ivar layout bitmaps. + void findGCClasses(SharedCache* cache, const macho_header

* header) + { + if (headerSupportsGC(cache, header)) { + ClassWalker > classVisitor(fGCClasses); + classVisitor.walk(cache, header); + } + } + // Enumerates objc classes in the module and performs any ivar slides void optimize(SharedCache* cache, const macho_header

* header) { - // The slide code cannot fix up GC layout strings so skip modules that support ore require GC - const macho_section

*imageInfoSection = header->getSection("__DATA", "__objc_imageinfo"); - if (imageInfoSection) { - objc_image_info *info = (objc_image_info *)cache->mappedAddressForVMAddress(imageInfoSection->addr()); - if (!info->supportsGCFlagSet() && !info->requiresGCFlagSet()) { - ClassWalker > classVisitor(*this); - classVisitor.walk(cache, header); - } else { - //fprintf(stderr, "GC support present - skipped module\n"); - } + if (! headerSupportsGC(cache, header)) { + ClassWalker > classVisitor(*this); + classVisitor.walk(cache, header); } } }; @@ -956,6 +1150,25 @@ class MethodListSorter { fOptimized++; } + void visitProtocolMethodList(objc_method_list_t *mlist, pint_t *typelist) + { + typename objc_method_t::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& mi = mlist->get(i); + objc_method_t& mj = mlist->get(j); + if (! sorter(mi, mj)) { + std::swap(mi, mj); + if (typelist) std::swap(typelist[i], typelist[j]); + } + } + } + + mlist->setFixedUp(); + fOptimized++; + } + public: MethodListSorter() : fOptimized(0) { } @@ -964,7 +1177,7 @@ public: void optimize(SharedCache* cache, macho_header

* header) { MethodListWalker > mw(*this); - mw.walk(cache, header, false /* don't sort protocol method lists*/); + mw.walk(cache, header); } }; @@ -985,7 +1198,9 @@ public: { if (count == 0) return NULL; - if (bufSize < 2*sizeof(uint32_t) + count*sizeof(objc_header_info_t)) { + size_t requiredSize = + 2*sizeof(uint32_t) + count*sizeof(objc_header_info_t); + if (bufSize < requiredSize) { return "libobjc's read/write section is too small (metadata not optimized)"; } @@ -994,9 +1209,8 @@ public: A::P::E::set32(buf32[1], sizeof(objc_header_info_t)); fHinfos = (objc_header_info_t*)(buf32+2); - size_t total = sizeof(uint32_t) + count*sizeof(objc_header_info_t); - buf += total; - bufSize -= total; + buf += requiredSize; + bufSize -= requiredSize; return NULL; } diff --git a/launch-cache/dsc_extractor.cpp b/launch-cache/dsc_extractor.cpp index b1fb172..e21a0f6 100644 --- a/launch-cache/dsc_extractor.cpp +++ b/launch-cache/dsc_extractor.cpp @@ -83,6 +83,16 @@ class NotReExportSymbol { public: NotReExportSymbol(const std::set &rd) :_reexportDeps(rd) {} bool operator()(const mach_o::trie::Entry &entry) const { + bool result = isSymbolReExport(entry); + if (result) { + // Xcode 6 leaks in dyld_shared_cache_extract_dylibs + ::free((void*)entry.name); + const_cast(&entry)->name = NULL; + } + return result; + } +private: + bool isSymbolReExport(const mach_o::trie::Entry &entry) const { if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) return true; if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 ) @@ -92,7 +102,6 @@ public: return true; return false; } -private: const std::set &_reexportDeps; }; @@ -357,6 +366,12 @@ int optimize_linkedit(macho_header* mh, uint64_t textOffsetInCach // return new size *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096); + // Xcode 6 leaks in dyld_shared_cache_extract_dylibs + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + ::free((void*)(it->name)); + } + + return 0; } diff --git a/launch-cache/dsc_iterator.cpp b/launch-cache/dsc_iterator.cpp index 477894b..2c7c212 100644 --- a/launch-cache/dsc_iterator.cpp +++ b/launch-cache/dsc_iterator.cpp @@ -108,6 +108,8 @@ namespace dyld { segInfo.name = segCmd->segname(); segInfo.fileOffset = fileOffset; segInfo.fileSize = sizem; + if ( segCmd->filesize() > segCmd->vmsize() ) + return -1; segInfo.address = segCmd->vmaddr(); callback(&dylibInfo, &segInfo); } diff --git a/launch-cache/dyld_cache_format.h b/launch-cache/dyld_cache_format.h index 9407ff6..6862581 100644 --- a/launch-cache/dyld_cache_format.h +++ b/launch-cache/dyld_cache_format.h @@ -44,6 +44,7 @@ struct dyld_cache_header uint64_t localSymbolsOffset; // file offset of where local symbols are stored uint64_t localSymbolsSize; // size of local symbols information uint8_t uuid[16]; // unique value for each shared cache file + uint64_t cacheType; // 1 for development, 0 for optimized }; struct dyld_cache_mapping_info { diff --git a/launch-cache/update_dyld_shared_cache.cpp b/launch-cache/update_dyld_shared_cache.cpp index cc6dc0c..4ab3694 100644 --- a/launch-cache/update_dyld_shared_cache.cpp +++ b/launch-cache/update_dyld_shared_cache.cpp @@ -68,7 +68,7 @@ #define SELOPT_WRITE #include "objc-shared-cache.h" -#define FIRST_DYLIB_TEXT_OFFSET 0x8000 +#define FIRST_DYLIB_TEXT_OFFSET 0x10000 #ifndef LC_FUNCTION_STARTS #define LC_FUNCTION_STARTS 0x26 @@ -77,6 +77,7 @@ static bool verbose = false; static bool progress = false; static bool iPhoneOS = false; +static bool rootless = true; static std::vector warnings; @@ -125,6 +126,7 @@ public: static void addArchPair(ArchPair ap); static void addRoot(const char* vpath, const std::set& archs); + static uint64_t maxCacheSizeForArchPair(ArchPair ap); static void findSharedDylibs(ArchPair ap); static ArchGraph* graphForArchPair(ArchPair ap) { return fgPerArchGraph[ap]; } static void setFileSystemRoot(const char* root) { fgFileSystemRoot = root; } @@ -148,6 +150,8 @@ private: const MachOLayoutAbstraction* getLayout() const { return fLayout; } size_t useCount() const { return fRootsDependentOnThis.size(); } bool allDependentsFound() const { return !fDependentMissing; } + bool dependsOnDylibList() const { return fRootsDependentOnThis.count(const_cast(this)); } + private: ArchGraph* fGraph; const char* fPath; @@ -486,16 +490,32 @@ void ArchGraph::DependencyNode::markNeededByRoot(ArchGraph::DependencyNode* root } + ArchGraph::DependencyNode::DependencyNode(ArchGraph* graph, const char* path, const MachOLayoutAbstraction* layout) : fGraph(graph), fPath(strdup(path)), fLayout(layout), fDependenciesLoaded(false), fDependentMissing(false) { //fprintf(stderr, "new DependencyNode(0x%08X, %s)\n", graph->fArch, path); } +uint64_t ArchGraph::maxCacheSizeForArchPair(ArchPair ap) { + switch ( ap.arch ) { + case CPU_TYPE_I386: + return 0x20000000; + case CPU_TYPE_X86_64: + return 0x40000000; + case CPU_TYPE_ARM: + return ARM_SHARED_REGION_SIZE; + case CPU_TYPE_ARM64: + return ARM64_SHARED_REGION_SIZE; + default: return UINT64_MAX; + } +} + void ArchGraph::findSharedDylibs(ArchPair ap) { const PathToNode& nodes = fgPerArchGraph[ap]->fNodes; std::set possibleLibs; + std::map layoutToNode; //fprintf(stderr, "shared for arch %s\n", archName(ap)); for(PathToNode::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { DependencyNode* node = it->second; @@ -506,9 +526,10 @@ void ArchGraph::findSharedDylibs(ArchPair ap) char* msg; if ( sharable(layout, ap, &msg) ) { possibleLibs.insert(layout); + layoutToNode[layout] = node; } else { - if ( layout->getID().name[0] == '@' ) { + if ( !iPhoneOS && (layout->getID().name[0] == '@') ) { // update_dyld_shared_cache should suppress warnings for embedded frameworks } else { @@ -523,10 +544,49 @@ void ArchGraph::findSharedDylibs(ArchPair ap) // prune so that all shareable libs depend only on other shareable libs std::set& sharedLibs = fgPerArchGraph[ap]->fSharedDylibs; std::map shareableMap; + uint64_t totalLibSize = 0; for (std::set::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 removableLibs; + + for (const MachOLayoutAbstraction* layout : sharedLibs) { + // Every library uses itself, and every MH_DYLIB has an extra useCount, so we know useCount of 2 implies nothing else in the shared cache uses it + if (layoutToNode[layout]->useCount() == 2) { + if ( layoutToNode[layout]->dependsOnDylibList() ) { + removableLibs.push_back(layout); + //fprintf(stderr, " possible to evict: %s\n", layout->getID().name); + } + } + } + + std::sort(removableLibs.begin(), removableLibs.end(), [](const MachOLayoutAbstraction* a, const MachOLayoutAbstraction* b){ + return a->getVMSize() < b->getVMSize(); + }); + + while ( (totalLibSize > maxCacheSize) && !removableLibs.empty() ) { + const MachOLayoutAbstraction* largestRemovableLib = removableLibs.back(); + removableLibs.pop_back(); + if ( largestRemovableLib->getVMSize() > 1024*1024 ) + fprintf(stderr, "update_dyld_shared_cache: evicting % 3lldMB leaf dylib %s\n", largestRemovableLib->getVMSize()/(1024*1024), largestRemovableLib->getID().name); + else + fprintf(stderr, "update_dyld_shared_cache: evicting % 3lldKB leaf dylib %s\n", largestRemovableLib->getVMSize()/1024, largestRemovableLib->getID().name); + sharedLibs.erase(largestRemovableLib); + totalLibSize -= largestRemovableLib->getVMSize(); + } + fprintf(stderr, "update_dyld_shared_cache: unoptimized %s shared cache reduced to total VM space: %lldMB\n", archName(ap), totalLibSize/1024/1024); + } +#endif } const char* ArchGraph::archName(ArchPair ap) @@ -571,20 +631,19 @@ const char* ArchGraph::archName(ArchPair ap) bool ArchGraph::sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char** msg) { + int trustErr = layout->notTrusted(); if ( ! layout->isTwoLevelNamespace() ) asprintf(msg, "can't put %s in shared cache because it was built -flat_namespace", layout->getID().name); else if ( ! layout->inSharableLocation() ) asprintf(msg, "can't put %s in shared cache because its -install_name is not in /usr/lib or /System/Library", layout->getID().name); else if ( ! layout->hasSplitSegInfo() ) asprintf(msg, "can't put %s in shared cache because it was not built for %s or later", layout->getID().name, (iPhoneOS ? "iPhoneOS 3.1" : "MacOSX 10.5")); - else if ( ! layout->isRootOwned() ) - asprintf(msg, "can't put %s in shared cache because it is not owned by root", layout->getID().name); + else if ( rootless == true && trustErr != 0 ) + asprintf(msg, "can't put %s in shared cache because it is not trusted: %s", layout->getFilePath(), strerror(trustErr)); else if ( layout->hasDynamicLookupLinkage() ) asprintf(msg, "can't put %s in shared cache because it was built with '-undefined dynamic_lookup'", layout->getID().name); else if ( layout->hasMainExecutableLookupLinkage() ) asprintf(msg, "can't put %s in shared cache because it was built with '-bundle_loader'", layout->getID().name); - else if ( layout->hasMultipleReadWriteSegments() ) - asprintf(msg, "can't put %s in shared cache because it has multiple r/w segments", layout->getID().name); else return true; return false; @@ -670,7 +729,7 @@ private: StringPool::StringPool() - : fBufferUsed(0), fBufferAllocated(64*1024*1024) + : fBufferUsed(0), fBufferAllocated(128*1024*1024) { fBuffer = (char*)malloc(fBufferAllocated); } @@ -733,6 +792,7 @@ public: bool alphaSort, bool verify, bool optimize, uint64_t dyldBaseAddress); bool update(bool force, bool optimize, bool deleteExistingFirst, int archIndex, int archCount, bool keepSignatures, bool dontMapLocalSymbols); + void writeCacheFile(const char *cacheFilePath, uint8_t *cacheFileBuffer, uint32_t cacheFileSize, bool deleteOldCache); static const char* cacheFileSuffix(bool optimized, const char* archName); // vm address = address AS WRITTEN into the cache @@ -869,7 +929,7 @@ class PointerSection SharedCache* const fCache; const macho_section

* const fSection; pint_t * const fBase; - uint64_t fCount; + pint_t fCount; public: PointerSection(SharedCache* cache, const macho_header

* header, @@ -881,25 +941,25 @@ public: { } - uint64_t count() const { return fCount; } + pint_t count() const { return fCount; } - uint64_t getUnmapped(uint64_t index) const { + pint_t getVMAddress(pint_t index) const { if (index >= fCount) throwf("index out of range"); return P::getP(fBase[index]); } - T get(uint64_t index) const { - return (T)fCache->mappedAddressForVMAddress(getUnmapped(index)); + T get(pint_t index) const { + return (T)fCache->mappedAddressForVMAddress(getVMAddress(index)); } - void set(uint64_t index, uint64_t value) { + void setVMAddress(pint_t index, pint_t value) { if (index >= fCount) throwf("index out of range"); P::setP(fBase[index], value); } void removeNulls() { - uint64_t shift = 0; - for (uint64_t i = 0; i < fCount; i++) { + pint_t shift = 0; + for (pint_t i = 0; i < fCount; i++) { pint_t value = fBase[i]; if (value) { fBase[i-shift] = value; @@ -1067,6 +1127,11 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const std::v else { fCacheFilePath = strdup(cachePathCanonical); } + + // If the path we are writing to is trusted then our sources need to be trusted + // Can't update the update_dyld_shared_cache on a non-boot volume + rootless = rootless_check_trusted(fCacheFilePath); + if ( overlayPaths.size() == 1 ) { // in overlay mode if there already is a cache file in the overlay, // check if it is up to date. @@ -1201,24 +1266,82 @@ void SharedCache::assignNewBaseAddresses(bool verify) // align __TEXT region currentExecuteAddress = regionAlign(currentExecuteAddress); - // layout DATA for dylibs +#define DENSE_PACK 0 + // layout __DATA* segments + std::vector dataSegs; + std::vector dataConstSegs; + std::vector dataDirtySegs; const uint64_t startWritableAddress = sharedRegionStartWritableAddress(currentExecuteAddress); uint64_t currentWritableAddress = startWritableAddress; - for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { - std::vector& 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); - // always 4KB align data pages to allow padding to be removed - currentWritableAddress = pageAlign4KB(seg.newAddress() + seg.size()); + seg.reset(); + if ( strcmp(seg.name(), "__DATA_CONST") == 0 ) + dataConstSegs.push_back(&seg); + else if ( strcmp(seg.name(), "__DATA_DIRTY") == 0 ) + dataDirtySegs.push_back(&seg); + else + dataSegs.push_back(&seg); } } } + // coalesce all __DATA_CONST segments + for (MachOLayoutAbstraction::Segment* seg : dataConstSegs) { + #if DENSE_PACK + // start segment at needed alignment + currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment()); + seg->setNewAddress(currentWritableAddress); + // pack together + uint64_t justSectionsSize = seg->sectionsSize(); + currentWritableAddress = seg->newAddress() + justSectionsSize; + seg->setSize(justSectionsSize); + if ( seg->fileSize() > justSectionsSize ) + seg->setFileSize(justSectionsSize); + #else + seg->setNewAddress(currentWritableAddress); + // pack to 4KB pages + currentWritableAddress = pageAlign4KB(seg->newAddress() + seg->size()); + #endif + } + #if DENSE_PACK + currentWritableAddress = pageAlign4KB(currentWritableAddress); + #endif + // coalesce all __DATA segments + for (MachOLayoutAbstraction::Segment* seg : dataSegs) { + #if DENSE_PACK + // start segment at needed alignment + currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment()); + seg->setNewAddress(currentWritableAddress); + // pack together + uint64_t justSectionsSize = seg->sectionsSize(); + currentWritableAddress = seg->newAddress() + justSectionsSize; + seg->setSize(justSectionsSize); + if ( seg->fileSize() > justSectionsSize ) + seg->setFileSize(justSectionsSize); + #else + seg->setNewAddress(currentWritableAddress); + // pack to 4KB pages + currentWritableAddress = pageAlign4KB(seg->newAddress() + seg->size()); + #endif + } + #if DENSE_PACK + currentWritableAddress = pageAlign4KB(currentWritableAddress); + #endif + // coalesce all __DATA_DIRTY segments + for (MachOLayoutAbstraction::Segment* seg : dataDirtySegs) { + // start segment at needed alignment + currentWritableAddress = (currentWritableAddress + seg->sectionsAlignment() - 1) & (-seg->sectionsAlignment()); + seg->setNewAddress(currentWritableAddress); + // pack together + uint64_t justSectionsSize = seg->sectionsSize(); + currentWritableAddress = seg->newAddress() + justSectionsSize; + seg->setSize(justSectionsSize); + if ( seg->fileSize() > justSectionsSize ) + seg->setFileSize(justSectionsSize); + } // align __DATA region currentWritableAddress = regionAlign(currentWritableAddress); @@ -1869,6 +1992,8 @@ void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t le for(macho_section

* sect = sectionsStart; sect < sectionsEnd; ++sect) { if ( sect->offset() != 0 ) sect->set_offset(sect->offset()+fileOffsetDelta); + //if ( (sect->flags() & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) + // fprintf(stderr, "found initializer(s) in %s\n", fLayout.getFilePath()); } } } @@ -1933,6 +2058,7 @@ void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t le switch ( srcCmd->cmd() ) { case LC_SEGMENT_SPLIT_INFO: case LC_DYLIB_CODE_SIGN_DRS: + case LC_RPATH: // don't copy break; case LC_CODE_SIGNATURE: @@ -2175,6 +2301,140 @@ public: }; +template +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>; + pint_t visitProtocolReference(SharedCache* cache, pint_t oldValue) + { + objc_protocol_t* proto = (objc_protocol_t*) + 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* cache, + const macho_header

* header) + { + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t *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)) { + 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* cache, + uint8_t *& dest, size_t& remaining, + std::vector& 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); + 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* oldProto = (objc_protocol_t*) + cache->mappedAddressForVMAddress(iter->second); + + // Create a new protocol object. + objc_protocol_t* proto = (objc_protocol_t*)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)); + // 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* cache, const macho_header

* header) + { + ProtocolReferenceWalker> refs(*this); + refs.walk(cache, header); + } + + objc_opt::string_map& protocolNames() { + return fProtocolNames; + } + + objc_opt::protocol_map& protocols() { + return fProtocols; + } + + size_t protocolCount() const { return fProtocolCount; } + size_t protocolReferenceCount() const { return fProtocolReferenceCount; } +}; + + static int percent(size_t num, size_t denom) { if (denom) return (int)(num / (double)denom * 100); else return 100; @@ -2194,14 +2454,17 @@ void SharedCache::optimizeObjC(std::vector& pointersInData) warn(archName(), "libobjc's optimization structure size is wrong (metadata not optimized)"); } - // Find libobjc's empty sections to fill in + // Find libobjc's empty sections to fill in. + // Find libobjc's list of pointers for us to use. const macho_section

*optROSection = NULL; const macho_section

*optRWSection = NULL; + const macho_section

*optPointerListSection = NULL; for(typename std::vector::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

* mh = (const macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); optROSection = mh->getSection("__TEXT", "__objc_opt_ro"); optRWSection = mh->getSection("__DATA", "__objc_opt_rw"); + optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs"); break; } } @@ -2215,6 +2478,11 @@ void SharedCache::optimizeObjC(std::vector& pointersInData) warn(archName(), "libobjc's read/write section missing (metadata not optimized)"); return; } + + if ( optPointerListSection == NULL ) { + warn(archName(), "libobjc's pointer list section missing (metadata not optimized)"); + return; + } uint8_t* optROData = (uint8_t*)mappedAddressForVMAddress(optROSection->addr()); size_t optRORemaining = optROSection->size(); @@ -2235,6 +2503,12 @@ void SharedCache::optimizeObjC(std::vector& pointersInData) return; } + if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt)) { + warn(archName(), "libobjc's pointer list section is too small (metadata not optimized)"); + return; + } + const objc_opt::objc_opt_pointerlist_tt *optPointerList = (const objc_opt::objc_opt_pointerlist_tt *)mappedAddressForVMAddress(optPointerListSection->addr()); + // Write nothing to optROHeader until everything else is written. // If something fails below, libobjc will not use the section. @@ -2334,11 +2608,57 @@ void SharedCache::optimizeObjC(std::vector& pointersInData) } + // Unique protocols and build protocol table. + + // This is SAFE: no protocol references are updated yet + // This must be done AFTER updating method lists. + + ProtocolOptimizer protocolOptimizer; + for(typename std::vector::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) { + const macho_header

*mh = (const macho_header

*)(*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::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) { + const macho_header

*mh = (const macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); + protocolOptimizer.updateReferences(this, mh); + } + + // Repair ivar offsets. // This is SAFE: the runtime always validates ivar offsets at runtime. IvarOffsetOptimizer ivarOffsetOptimizer; + for(typename std::vector::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) { + const macho_header

*mh = (const macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); + ivarOffsetOptimizer.findGCClasses(this, mh); + } for(typename std::vector::const_iterator it = sizeSortedDylibs.begin(); it != sizeSortedDylibs.end(); ++it) { const macho_header

*mh = (const macho_header

*)(*it->layout).getSegments()[0].mappedAddress(); ivarOffsetOptimizer.optimize(this, mh); @@ -2364,6 +2684,7 @@ void SharedCache::optimizeObjC(std::vector& pointersInData) // Success. Update RO header last. E::set32(optROHeader->selopt_offset, seloptVMAddr - optROSection->addr()); E::set32(optROHeader->clsopt_offset, clsoptVMAddr - optROSection->addr()); + E::set32(optROHeader->protocolopt_offset, protocoloptVMAddr - optROSection->addr()); E::set32(optROHeader->headeropt_offset, hinfoVMAddr - optROSection->addr()); if ( verbose ) { @@ -2383,6 +2704,12 @@ void SharedCache::optimizeObjC(std::vector& pointersInData) fprintf(stderr, "update_dyld_shared_cache: for %s, " "updated %zu selector references\n", archName(), uniq.count()); + fprintf(stderr, "update_dyld_shared_cache: for %s, " + "uniqued %zu protocols\n", + archName(), protocolOptimizer.protocolCount()); + fprintf(stderr, "update_dyld_shared_cache: for %s, " + "updated %zu protocol references\n", + archName(), protocolOptimizer.protocolReferenceCount()); fprintf(stderr, "update_dyld_shared_cache: for %s, " "updated %zu ivar offsets\n", archName(), ivarOffsetOptimizer.optimized()); @@ -2476,12 +2803,108 @@ static bool adhoc_codesign_share_cache(const char* path) return true; } +template +void SharedCache::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); + // 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*)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::addCacheSlideInfo(){ return true; } template <> bool SharedCache::addCacheSlideInfo() { return true; } template <> bool SharedCache::addCacheSlideInfo() { return false; } -template <> bool SharedCache::addCacheSlideInfo() { return true; } +template <> bool SharedCache::addCacheSlideInfo() { return true; } template @@ -2489,6 +2912,12 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, int archCount, bool keepSignatures, bool dontMapLocalSymbols) { bool didUpdate = false; + bool canEmitDevelopmentCache = true; + char devCacheFilePath[strlen(fCacheFilePath)+strlen(".development")]; + char fileListFilePath[strlen(fCacheFilePath)+strlen(".list")]; + sprintf(devCacheFilePath, "%s.development", fCacheFilePath); + sprintf(fileListFilePath, "%s.list", fCacheFilePath); + std::vector paths; // already up to date? if ( force || fExistingIsNotUpToDate ) { @@ -2504,8 +2933,6 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, ::unlink(fCacheFilePath); uint8_t* inMemoryCache = NULL; uint32_t allocatedCacheSize = 0; - char tempCachePath[strlen(fCacheFilePath)+16]; - sprintf(tempCachePath, "%s.tmp%u", fCacheFilePath, getpid()); try { // allocate a memory block to hold cache uint32_t cacheFileSize = 0; @@ -2578,11 +3005,12 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, const int dylibCount = fDylibs.size(); int dylibIndex = 0; int progressIndex = 0; + bool foundLibSystem = false; for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++dylibIndex) { const char* path = it->layout->getFilePath(); int src = ::open(path, O_RDONLY, 0); if ( src == -1 ) - throwf("can't open file %s, errnor=%d", it->layout->getID().name, errno); + throwf("can't open file %s, errno=%d", it->layout->getID().name, errno); // mark source as "don't cache" (void)fcntl(src, F_NOCACHE, 1); // verify file has not changed since dependency analysis @@ -2593,15 +3021,18 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, throwf("file inode changed from %llu to %llu during cache creation: %s", it->layout->getInode(), stat_buf.st_ino, path); else if ( it->layout->getLastModTime() != stat_buf.st_mtime ) throwf("file mtime changed from 0x%lX to 0x%lX during cache creation: %s", it->layout->getLastModTime(), stat_buf.st_mtime, path); - + if ( strcmp(it->layout->getID().name, "/usr/lib/libSystem.B.dylib") == 0 ) + foundLibSystem = true; if ( verbose ) fprintf(stderr, "update_dyld_shared_cache: copying %s to cache\n", it->layout->getFilePath()); try { const std::vector& segs = it->layout->getSegments(); for (int i=0; i < segs.size(); ++i) { const MachOLayoutAbstraction::Segment& seg = segs[i]; - if ( verbose ) - fprintf(stderr, "\t\tsegment %s, size=0x%0llX, cache address=0x%0llX\n", seg.name(), seg.fileSize(), seg.newAddress()); + if ( verbose ) { + fprintf(stderr, "\t\tsegment %s, size=0x%0llX, cache address=0x%0llX, buffer address=%p\n", + seg.name(), seg.size(), seg.newAddress(), &inMemoryCache[cacheFileOffsetForVMAddress(seg.newAddress())]); + } if ( seg.size() > 0 ) { const uint64_t segmentSrcStartOffset = it->layout->getOffsetInUniversalFile()+seg.fileOffset(); const uint64_t segmentSize = seg.fileSize(); @@ -2621,6 +3052,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, throwf("%s while copying %s to shared cache", msg, it->layout->getID().name); } ::close(src); + paths.push_back(it->layout->getID().name); if ( progress ) { // assuming read takes 40% of time int nextProgressIndex = archIndex*100+(40*dylibIndex)/dylibCount; @@ -2629,7 +3061,9 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, progressIndex = nextProgressIndex; } } - + if ( !foundLibSystem ) + throw "cache would be missing required dylib /usr/lib/libSystem.B.dylib"; + // set mapped address for each segment for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); @@ -2649,7 +3083,10 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { try { Rebaser r(*it->layout); - r.rebase(pointersInData); + if (!r.rebase(pointersInData)) { + canEmitDevelopmentCache = false; + fprintf(stderr, "update_dyld_shared_cache: Omitting development cache for %s, cannot rebase dylib into place for %s\n", archName(), it->layout->getID().name); + } //if ( verbose ) // fprintf(stderr, "update_dyld_shared_cache: for %s, rebasing dylib into cache for %s\n", archName(), it->layout->getID().name); } @@ -2665,13 +3102,12 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, std::vector*> binders; for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { //fprintf(stderr, "binding %s\n", it->layout->getID().name); - Binder* binder = new Binder(*it->layout, fDyldBaseAddress); + Binder* binder = new Binder(*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*>::iterator it = binders.begin(); it != binders.end(); ++it) { (*it)->setDependentBinders(map); @@ -2687,6 +3123,46 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, throwf("%s in %s", msg, (*it)->getDylibID()); } } + + for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + const macho_header

* fHeader = (const macho_header

*)it->layout->getSegments()[0].mappedAddress(); + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)fHeader + sizeof(macho_header

)); + const uint32_t cmd_count = fHeader->ncmds(); + const macho_load_command

* cmd = cmds; + macho_dyld_info_command

* fDyldInfo; + uint64_t originalLinkEditVMAddr = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == macho_segment_command

::CMD ) { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + if ( strcmp(seg->segname(), "__LINKEDIT") != 0 ) { + pint_t oldFileOff = seg->fileoff(); + originalLinkEditVMAddr += seg->vmsize(); + // don't alter __TEXT until 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

* const sectionsStart = (macho_section

*)((char*)seg + sizeof(macho_segment_command

)); + macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; + for(macho_section

* 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

*)cmd; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + } + // optimize binding for(typename std::vector*>::iterator it = binders.begin(); it != binders.end(); ++it) { try { @@ -2696,6 +3172,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, throwf("%s in %s", msg, (*it)->getDylibID()); } } + // delete binders for(typename std::vector*>::iterator it = binders.begin(); it != binders.end(); ++it) { delete *it; @@ -2703,9 +3180,11 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, // merge/optimize all LINKEDIT segments if ( optimize ) { - //fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024)); + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024)); cacheFileSize = (this->optimizeLINKEDIT(keepSignatures, dontMapLocalSymbols) - inMemoryCache); - //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size 0x%08X %uMB\n", cacheFileSize, cacheFileSize/(1024*1024)); + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024)); // update header to reduce mapping size dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&inMemoryCache[sizeof(dyldCacheHeader)]; @@ -2718,7 +3197,22 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, // header->codeSignatureOffset(), fMappings.back().sfm_address + fMappings.back().sfm_size); header->set_codeSignatureOffset(fMappings.back().sfm_file_offset + fMappings.back().sfm_size); } - + + // dump dev cache with optimized linkedit, but not ObjC optimizations + if (iPhoneOS && canEmitDevelopmentCache) { + int fileListFD = ::open(fileListFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if ( fileListFD != -1 ) { + for (const char* path : paths) { + write(fileListFD, path, strlen(path)+1); + write(fileListFD, "\n", 1); + } + close(fileListFD); + } + + ((dyldCacheHeader*)inMemoryCache)->set_cacheType(1); + writeCacheFile(devCacheFilePath, inMemoryCache, cacheFileSize, fCacheFileInFinalLocation); + } + // unique objc selectors and update other objc metadata if ( optimize ) { optimizeObjC(pointersInData); @@ -2859,20 +3353,12 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, for (int i=0; i < cacheHeader->mappingCount(); ++i) { uint64_t endAddr = mappings[i].address() + mappings[i].size() + estCodeSigSize; if ( endAddr > (sharedRegionStartAddress() + sharedRegionSize()) ) { - throwf("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regionsaddress space. Overflow amount: %lluKB\n", + throwf("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regions address space. Overflow amount: %lluKB\n", getpid(), fArchGraph->archName(), (endAddr-(sharedRegionStartAddress() + sharedRegionSize()))/1024); } } } - // compute UUID of whole cache - uint8_t digest[16]; - CC_MD5(inMemoryCache, cacheFileSize, digest); - // 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*)inMemoryCache)->set_uuid(digest); - if ( fVerify ) { // if no existing cache, say so if ( fExistingCacheForVerification == NULL ) { @@ -2935,98 +3421,9 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, } } else { - // install signal handlers to delete temp file if program is killed - sCleanupFile = tempCachePath; - ::signal(SIGINT, cleanup); - ::signal(SIGBUS, cleanup); - ::signal(SIGSEGV, cleanup); - - // create var/db/dyld dirs if needed - char dyldDirs[1024]; - strcpy(dyldDirs, fCacheFilePath); - char* lastSlash = strrchr(dyldDirs, '/'); - if ( lastSlash != NULL ) - lastSlash[1] = '\0'; - struct stat stat_buf; - if ( stat(dyldDirs, &stat_buf) != 0 ) { - const char* afterSlash = &dyldDirs[1]; - char* slash; - while ( (slash = strchr(afterSlash, '/')) != NULL ) { - *slash = '\0'; - ::mkdir(dyldDirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); - *slash = '/'; - afterSlash = slash+1; - } - } - - // create temp file for cache - int fd = ::open(tempCachePath, O_CREAT | O_RDWR | O_TRUNC, 0644); - if ( fd == -1 ) - throwf("can't create temp file %s, errnor=%d", tempCachePath, errno); - - // try to allocate whole cache file contiguously - fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, cacheFileSize, 0 }; - ::fcntl(fd, F_PREALLOCATE, &fcntlSpec); - - // write out cache file - if ( verbose ) - fprintf(stderr, "update_dyld_shared_cache: writing cache to disk: %s\n", tempCachePath); - if ( ::pwrite(fd, inMemoryCache, cacheFileSize, 0) != cacheFileSize ) - throwf("write() failure creating cache file, errno=%d", errno); - if ( progress ) { - // assuming write takes 35% of time - fprintf(stdout, "%3u/%u\n", (archIndex+1)*90, archCount*100); - } - - // flush to disk and close - int result = ::fcntl(fd, F_FULLFSYNC, NULL); - if ( result == -1 ) - fprintf(stderr, "update_dyld_shared_cache: warning, fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, tempCachePath); - result = ::close(fd); - if ( result != 0 ) - fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath); - - if ( !iPhoneOS ) - adhoc_codesign_share_cache(tempCachePath); - - // 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*)inMemoryCache)->set_cacheType(0); + writeCacheFile(fCacheFilePath, inMemoryCache, cacheFileSize, fCacheFileInFinalLocation); didUpdate = true; - - // restore default signal handlers - ::signal(SIGINT, SIG_DFL); - ::signal(SIGBUS, SIG_DFL); - ::signal(SIGSEGV, SIG_DFL); - // generate human readable "map" file that shows the layout of the cache file if ( verbose ) fprintf(stderr, "update_dyld_shared_cache: writing .map file to disk\n"); @@ -3036,7 +3433,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, sprintf(tempMapFilePath, "%s.map%u", fCacheFilePath, getpid()); FILE* fmap = ::fopen(tempMapFilePath, "w"); if ( fmap == NULL ) { - fprintf(stderr, "can't create map file %s, errnor=%d", tempCachePath, errno); + fprintf(stderr, "can't create map file %s, errno=%d", tempMapFilePath, errno); } else { for(std::vector::iterator it = fMappings.begin(); it != fMappings.end(); ++it) { @@ -3135,7 +3532,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, } } fclose(fmap); - result = ::rename(tempMapFilePath, mapFilePath); + ::rename(tempMapFilePath, mapFilePath); } } @@ -3148,8 +3545,6 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, } } catch (...){ - // remove temp cache file - ::unlink(tempCachePath); // remove in memory cache if ( inMemoryCache != NULL ) vm_deallocate(mach_task_self(), (vm_address_t)inMemoryCache, allocatedCacheSize); diff --git a/launch-cache/update_dyld_shared_cache_entitlements.plist b/launch-cache/update_dyld_shared_cache_entitlements.plist new file mode 100644 index 0000000..5ed9d1c --- /dev/null +++ b/launch-cache/update_dyld_shared_cache_entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.rootless.storage.dyld + + + diff --git a/libdyld.xcconfig b/libdyld.xcconfig deleted file mode 100644 index 649af26..0000000 --- a/libdyld.xcconfig +++ /dev/null @@ -1,8 +0,0 @@ - -LIBSYSTEM_LIBS[sdk=*simulator*] = -Wl,-upward-lsystem_sim_c -Wl,-upward-lSystem -LIBSYSTEM_LIBS[sdk=iphoneos*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-llaunch -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -LIBSYSTEM_LIBS[sdk=macosx*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-llaunch -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel - -INSTALL_PATH_ACTUAL = /usr/lib/system - - diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 5b297f3..657677a 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -883,11 +883,10 @@ void ImageLoader::weakBind(const LinkContext& context) } } } - if ( context.verboseWeakBind ) - dyld::log("dyld: weak binding all uses of %s to copy from %s\n", nameToCoalesce, targetImage->getShortName()); - // tell each to bind to this symbol (unless already bound) if ( targetAddr != 0 ) { + if ( context.verboseWeakBind ) + dyld::log("dyld: weak binding all uses of %s to copy from %s\n", nameToCoalesce, targetImage->getShortName()); for(int i=0; i < count; ++i) { if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { if ( context.verboseWeakBind ) diff --git a/src/ImageLoader.h b/src/ImageLoader.h index 6f4c3bc..877044c 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -104,8 +104,8 @@ #define TEXT_RELOC_SUPPORT __i386__ #define DYLD_SHARED_CACHE_SUPPORT (__arm__ || __arm64__) #define SUPPORT_OLD_CRT_INITIALIZATION 0 - #define SUPPORT_LC_DYLD_ENVIRONMENT 0 - #define SUPPORT_VERSIONED_PATHS 0 + #define SUPPORT_LC_DYLD_ENVIRONMENT 1 + #define SUPPORT_VERSIONED_PATHS 1 #define SUPPORT_CLASSIC_MACHO __arm__ #define SUPPORT_ZERO_COST_EXCEPTIONS (!__USING_SJLJ_EXCEPTIONS__) #define INITIAL_IMAGE_COUNT 256 @@ -291,6 +291,7 @@ public: bool linkingMainExecutable; bool startedInitializingMainExecutable; bool processIsRestricted; + bool processRequiresLibraryValidation; bool verboseOpts; bool verboseEnv; bool verboseMapping; diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index 53b0ddd..d552ef3 100644 --- a/src/ImageLoaderMachO.cpp +++ b/src/ImageLoaderMachO.cpp @@ -60,6 +60,15 @@ extern "C" long __stack_chk_guard; #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ #endif +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + + // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables #if __LP64__ #define LC_SEGMENT_COMMAND LC_SEGMENT_64 @@ -83,9 +92,9 @@ uint32_t ImageLoaderMachO::fgSymbolTrieSearchs = 0; ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, uint32_t segOffsets[], unsigned int libCount) - : ImageLoader(path, libCount), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), + : ImageLoader(path, libCount), fCoveredCodeLength(0), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), fEHFrameSectionOffset(0), fUnwindInfoSectionOffset(0), fDylibIDOffset(0), - fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), +fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), #if TEXT_RELOC_SUPPORT fTextSegmentRebases(false), fTextSegmentBinds(false), @@ -119,26 +128,25 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns // determine if this mach-o file has classic or compressed LINKEDIT and number of segments it has -void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed, +void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed, unsigned int* segCount, unsigned int* libCount, const LinkContext& context, - const linkedit_data_command** codeSigCmd) + const linkedit_data_command** codeSigCmd, + const encryption_info_command** encryptCmd) { *compressed = false; *segCount = 0; *libCount = 0; *codeSigCmd = NULL; - struct macho_segment_command* segCmd; - bool foundLoadCommandSegment = false; - uint32_t loadCommandSegmentIndex = 0xFFFFFFFF; - uintptr_t loadCommandSegmentVMStart = 0; - uintptr_t loadCommandSegmentVMEnd = 0; + *encryptCmd = NULL; const uint32_t cmd_count = mh->ncmds; const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + mh->sizeofcmds); const struct load_command* cmd = startCmds; + bool foundLoadCommandSegment = false; for (uint32_t i = 0; i < cmd_count; ++i) { uint32_t cmdLength = cmd->cmdsize; + struct macho_segment_command* segCmd; if ( cmdLength < 8 ) { dyld::throwf("malformed mach-o image: load command #%d length (%u) too small in %s", i, cmdLength, path); @@ -155,25 +163,56 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat break; case LC_SEGMENT_COMMAND: segCmd = (struct macho_segment_command*)cmd; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // rdar://problem/19617624 allow unmapped segments on OSX (but not iOS) + if ( (segCmd->filesize > segCmd->vmsize) && (segCmd->vmsize != 0) ) +#else + if ( segCmd->filesize > segCmd->vmsize ) +#endif + dyld::throwf("malformed mach-o image: segment load command %s filesize is larger than vmsize", segCmd->segname); // ignore zero-sized segments if ( segCmd->vmsize != 0 ) *segCount += 1; - // 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) ) { + // 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: @@ -188,36 +227,68 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat case LC_CODE_SIGNATURE: *codeSigCmd = (struct linkedit_data_command*)cmd; // only support one LC_CODE_SIGNATURE per image break; + case LC_ENCRYPTION_INFO: + case LC_ENCRYPTION_INFO_64: + *encryptCmd = (struct encryption_info_command*)cmd; // only support one LC_ENCRYPTION_INFO[_64] per image + break; } cmd = nextCmd; } - + if ( context.codeSigningEnforced && !foundLoadCommandSegment ) throw "load commands not in a segment"; - // verify another segment does not over-map load commands - cmd = startCmds; + + // verify every segment does not overlap another segment if ( context.codeSigningEnforced ) { + uintptr_t lastFileStart = 0; + uintptr_t linkeditFileStart = 0; + const struct load_command* cmd1 = startCmds; for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SEGMENT_COMMAND: - if ( i != loadCommandSegmentIndex ) { - segCmd = (struct macho_segment_command*)cmd; - uintptr_t start = segCmd->vmaddr; - uintptr_t end = segCmd->vmaddr + segCmd->vmsize; - if ( ((start <= loadCommandSegmentVMStart) && (end > loadCommandSegmentVMStart)) - || ((start >= loadCommandSegmentVMStart) && (start < loadCommandSegmentVMEnd)) ) - dyld::throwf("malformed mach-o image: segment %s overlaps load commands", segCmd->segname); + if ( cmd1->cmd == LC_SEGMENT_COMMAND ) { + struct macho_segment_command* segCmd1 = (struct macho_segment_command*)cmd1; + uintptr_t vmStart1 = segCmd1->vmaddr; + uintptr_t vmEnd1 = segCmd1->vmaddr + segCmd1->vmsize; + uintptr_t fileStart1 = segCmd1->fileoff; + uintptr_t fileEnd1 = segCmd1->fileoff + segCmd1->filesize; + + if (fileStart1 > lastFileStart) + lastFileStart = fileStart1; + + if ( strcmp(&segCmd1->segname[0], "__LINKEDIT") == 0 ) { + linkeditFileStart = fileStart1; + } + + const struct load_command* cmd2 = startCmds; + for (uint32_t j = 0; j < cmd_count; ++j) { + if ( cmd2 == cmd1 ) + continue; + if ( cmd2->cmd == LC_SEGMENT_COMMAND ) { + struct macho_segment_command* segCmd2 = (struct macho_segment_command*)cmd2; + uintptr_t vmStart2 = segCmd2->vmaddr; + uintptr_t vmEnd2 = segCmd2->vmaddr + segCmd2->vmsize; + uintptr_t fileStart2 = segCmd2->fileoff; + uintptr_t fileEnd2 = segCmd2->fileoff + segCmd2->filesize; + if ( ((vmStart2 <= vmStart1) && (vmEnd2 > vmStart1) && (vmEnd1 > vmStart1)) + || ((vmStart2 >= vmStart1) && (vmStart2 < vmEnd1) && (vmEnd2 > vmStart2)) ) + dyld::throwf("malformed mach-o image: segment %s vm overlaps segment %s", segCmd1->segname, segCmd2->segname); + if ( ((fileStart2 <= fileStart1) && (fileEnd2 > fileStart1) && (fileEnd1 > fileStart1)) + || ((fileStart2 >= fileStart1) && (fileStart2 < fileEnd1) && (fileEnd2 > fileStart2)) ) + dyld::throwf("malformed mach-o image: segment %s file content overlaps segment %s", segCmd1->segname, segCmd2->segname); } - break; + cmd2 = (const struct load_command*)(((char*)cmd2)+cmd2->cmdsize); + } } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + cmd1 = (const struct load_command*)(((char*)cmd1)+cmd1->cmdsize); } + + if (lastFileStart != linkeditFileStart) + dyld::throwf("malformed mach-o image: __LINKEDIT must be last segment"); } - + // fSegmentsArrayCount is only 8-bits if ( *segCount > 255 ) dyld::throwf("malformed mach-o image: more than 255 segments in %s", path); - + // fSegmentsArrayCount is only 8-bits if ( *libCount > 4095 ) dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path); @@ -237,7 +308,8 @@ ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, context, &codeSigCmd); + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); @@ -269,13 +341,14 @@ ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, con unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount, context, &codeSigCmd); + const encryption_info_command* encryptCmd; + sniffLoadCommands((const macho_header*)fileData, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); // instantiate concrete class based on content of load commands if ( compressed ) - return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); + return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, dataSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, encryptCmd, context); else #if SUPPORT_CLASSIC_MACHO - return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); + return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, dataSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); #else throw "missing LC_DYLD_INFO load command"; #endif @@ -289,7 +362,8 @@ ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, cons unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, context, &codeSigCmd); + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, path, true, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); @@ -308,7 +382,8 @@ ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, con unsigned int segCount; unsigned int libCount; const linkedit_data_command* sigcmd; - sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount, context, &sigcmd); + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, moduleName, false, &compressed, &segCount, &libCount, context, &sigcmd, &encryptCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); @@ -337,13 +412,16 @@ int ImageLoaderMachO::crashIfInvalidCodeSignature() } -void ImageLoaderMachO::parseLoadCmds() +void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) { // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide for(unsigned int i=0; i < fSegmentsCount; ++i) { // set up pointer to __LINKEDIT segment - if ( strcmp(segName(i),"__LINKEDIT") == 0 ) + if ( strcmp(segName(i),"__LINKEDIT") == 0 ) { + if ( context.codeSigningEnforced && (segFileOffset(i) > fCoveredCodeLength)) + dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName()); fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i)); + } #if TEXT_RELOC_SUPPORT // __TEXT segment always starts at beginning of file and contains mach_header and load commands if ( segExecutable(i) ) { @@ -451,6 +529,8 @@ void ImageLoaderMachO::parseLoadCmds() break; case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: minOSVersionCmd = (version_min_command*)cmd; break; default: @@ -792,6 +872,8 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod dyld::throwf("required code signature missing for '%s'\n", this->getPath()); } #endif + //Since we don't have a range for the signature we have to assume full coverage + fCoveredCodeLength = UINT64_MAX; } else { #if __MAC_OS_X_VERSION_MIN_REQUIRED @@ -804,15 +886,54 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of CD in mach-o file siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD - int result = fcntl(fd, F_ADDFILESIGS, &siginfo); + int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); + +#if TARGET_IPHONE_SIMULATOR + // rdar://problem/18759224> check range covered by the code directory after loading + // Attempt to fallback only if we are in the simulator + + if ( result == -1 ) { + result = fcntl(fd, F_ADDFILESIGS, &siginfo); + siginfo.fs_file_start = codeSigCmd->dataoff; + } +#endif + if ( result == -1 ) { if ( (errno == EPERM) || (errno == EBADEXEC) ) dyld::throwf("code signature invalid for '%s'\n", this->getPath()); if ( context.verboseCodeSignatures ) dyld::log("dyld: Failed registering code signature for %s, errno=%d\n", this->getPath(), errno); + siginfo.fs_file_start = UINT64_MAX; + } else if ( context.verboseCodeSignatures ) { + dyld::log("dyld: Registered code signature for %s\n", this->getPath()); + } + fCoveredCodeLength = siginfo.fs_file_start; + } +} + +void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // rdar://problem/21839703> 15A226d: dyld crashes in mageLoaderMachO::validateFirstPages during dlopen() after encountering an mmap failure + // We need to ignore older code signatures because they will be bad. + if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { + return; + } +#endif + if (codeSigCmd != NULL) { + if ( context.verboseMapping ) + dyld::log("dyld: validate pages: %llu\n", (unsigned long long)offsetInFat); + + void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, MAP_SHARED, fd, offsetInFat); + if ( fdata == MAP_FAILED ) { + if ( context.processRequiresLibraryValidation ) + dyld::throwf("cannot load image with wrong team ID in process using Library Validation"); else - dyld::log("dyld: Registered code signature for %s\n", this->getPath()); + dyld::throwf("mmap() errno=%d validating first page of '%s'", errno, getInstallPath()); } + if ( memcmp(fdata, fileData, lenFileData) != 0 ) + dyld::throwf("mmap() page compare failed for '%s'", getInstallPath()); + munmap(fdata, lenFileData); } } @@ -880,6 +1001,8 @@ uint32_t ImageLoaderMachO::sdkVersion() const switch ( cmd->cmd ) { case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: versCmd = (version_min_command*)cmd; return versCmd->sdk; } @@ -898,6 +1021,8 @@ uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh) switch ( cmd->cmd ) { case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: versCmd = (version_min_command*)cmd; return versCmd->version; } @@ -1077,7 +1202,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorpath.offset; if ( (strncmp(path, "@loader_path", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) { - if ( context.processIsRestricted && (context.mainExecutable == this) ) { + if ( context.processIsRestricted && !context.processRequiresLibraryValidation && (context.mainExecutable == this) ) { dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath()); break; } @@ -1093,7 +1218,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorgetPath()); break; } @@ -1108,7 +1233,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorgetPath()); break; } @@ -1543,13 +1668,13 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) for (uint32_t i = 0; i < cmd_count; ++i) { if ( cmd->cmd == LC_SEGMENT_COMMAND ) { const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - if ( strcmp(seg->segname, "__DATA") == 0 ) { + if ( strncmp(seg->segname, "__DATA", 6) == 0 ) { const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); const struct macho_section* const sectionsEnd = §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; diff --git a/src/ImageLoaderMachO.h b/src/ImageLoaderMachO.h index 4505d63..72c90f1 100644 --- a/src/ImageLoaderMachO.h +++ b/src/ImageLoaderMachO.h @@ -41,13 +41,15 @@ class ImageLoaderMachO : public ImageLoader { public: static ImageLoader* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context); - static ImageLoader* instantiateFromFile(const char* path, int fd, const uint8_t firstPage[4096], uint64_t offsetInFat, + static ImageLoader* instantiateFromFile(const char* path, int fd, const uint8_t firstPages[4096], uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, const LinkContext& context); static ImageLoader* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context); static ImageLoader* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context); bool inSharedCache() const { return fInSharedCache; } + void disableCoverageCheck() { fCoveredCodeLength = UINT64_MAX; } + const char* getInstallPath() const; virtual void* getMain() const; virtual void* getThreadPC() const; @@ -156,13 +158,15 @@ protected: protected: void destroy(); - static void sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed, + static void sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed, unsigned int* segCount, unsigned int* libCount, const LinkContext& context, - const linkedit_data_command** codeSigCmd); + const linkedit_data_command** codeSigCmd, + const encryption_info_command** encryptCmd); static bool needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh); void loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context); + void validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context); const struct macho_segment_command* segLoadCommand(unsigned int segIndex) const; - void parseLoadCmds(); + void parseLoadCmds(const ImageLoader::LinkContext& context); int crashIfInvalidCodeSignature(); bool segHasRebaseFixUps(unsigned int) const; bool segHasBindFixUps(unsigned int) const; @@ -198,7 +202,8 @@ protected: const LinkContext& context, bool runResolver) const; static uintptr_t bindLazySymbol(const mach_header*, uintptr_t* lazyPointer); -protected: +protected: + uint64_t fCoveredCodeLength; const uint8_t* fMachOData; const uint8_t* fLinkEditBase; // add any internal "offset" to this to get mapped address uintptr_t fSlide; diff --git a/src/ImageLoaderMachOClassic.cpp b/src/ImageLoaderMachOClassic.cpp index 8190f1d..4a0f5b3 100644 --- a/src/ImageLoaderMachOClassic.cpp +++ b/src/ImageLoaderMachOClassic.cpp @@ -97,7 +97,8 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(cons // for PIE record end of program, to know where to start loading dylibs if ( slide != 0 ) fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); - + + image->disableCoverageCheck(); image->instantiateFinish(context); image->setMapped(context); @@ -126,7 +127,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(cons } // create image by mapping in a mach-o file -ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, unsigned int segCount, unsigned int libCount, const struct linkedit_data_command* codeSigCmd, const LinkContext& context) @@ -139,6 +140,9 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char // if this image is code signed, let kernel validate signature before mapping any pages from image image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); + // Validate that first data we read with pread actually matches with code signature + image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context); + // mmap segments image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context); @@ -192,6 +196,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const mac // remember this is from shared cache and cannot be unloaded image->fInSharedCache = true; image->setNeverUnload(); + image->disableCoverageCheck(); // segments already mapped in cache if ( context.verboseMapping ) { @@ -230,6 +235,8 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromMemory(const ch // for compatibility, never unload dylibs loaded from memory image->setNeverUnload(); + image->disableCoverageCheck(); + // bundle loads need path copied if ( moduleName != NULL ) image->setPath(moduleName); @@ -273,7 +280,7 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateStart(const macho_h void ImageLoaderMachOClassic::instantiateFinish(const LinkContext& context) { // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide - this->parseLoadCmds(); + this->parseLoadCmds(context); } ImageLoaderMachOClassic::~ImageLoaderMachOClassic() diff --git a/src/ImageLoaderMachOClassic.h b/src/ImageLoaderMachOClassic.h index 88db64c..071bea3 100644 --- a/src/ImageLoaderMachOClassic.h +++ b/src/ImageLoaderMachOClassic.h @@ -39,7 +39,7 @@ class ImageLoaderMachOClassic : public ImageLoaderMachO { public: static ImageLoaderMachOClassic* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, unsigned int segCount, unsigned int libCount, const LinkContext& context); - static ImageLoaderMachOClassic* instantiateFromFile(const char* path, int fd, const uint8_t* fileData, + static ImageLoaderMachOClassic* instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, unsigned int segCount, unsigned int libCount, const struct linkedit_data_command* codeSigCmd, const LinkContext& context); diff --git a/src/ImageLoaderMachOCompressed.cpp b/src/ImageLoaderMachOCompressed.cpp index 6a3a535..329b81d 100644 --- a/src/ImageLoaderMachOCompressed.cpp +++ b/src/ImageLoaderMachOCompressed.cpp @@ -23,18 +23,21 @@ */ +#if __arm__ || __arm64__ + #include +#else + #include +#endif #include #include #include #include #include #include -#include #include #include #include #include - #include "ImageLoaderMachOCompressed.h" #include "mach-o/dyld_images.h" @@ -112,10 +115,11 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutabl // for PIE record end of program, to know where to start loading dylibs if ( slide != 0 ) fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); - + + image->disableCoverageCheck(); image->instantiateFinish(context); image->setMapped(context); - + if ( context.verboseMapping ) { dyld::log("dyld: Main executable mapped %s\n", path); for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { @@ -131,10 +135,12 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutabl } // create image by mapping in a mach-o file -ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, unsigned int segCount, unsigned int libCount, - const struct linkedit_data_command* codeSigCmd, const LinkContext& context) + const struct linkedit_data_command* codeSigCmd, + const struct encryption_info_command* encryptCmd, + const LinkContext& context) { ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount); @@ -145,9 +151,15 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons // if this image is code signed, let kernel validate signature before mapping any pages from image image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); + // Validate that first data we read with pread actually matches with code signature + image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context); + // mmap segments image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); + // if framework is FairPlay encrypted, register with kernel + image->registerEncryption(encryptCmd, context); + // probe to see if code signed correctly image->crashIfInvalidCodeSignature(); @@ -209,6 +221,7 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(con image->fInSharedCache = true; image->setNeverUnload(); image->setSlide(slide); + image->disableCoverageCheck(); // segments already mapped in cache if ( context.verboseMapping ) { @@ -247,6 +260,8 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(co // for compatibility, never unload dylibs loaded from memory image->setNeverUnload(); + image->disableCoverageCheck(); + // bundle loads need path copied if ( moduleName != NULL ) image->setPath(moduleName); @@ -297,7 +312,7 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const m void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context) { // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide - this->parseLoadCmds(); + this->parseLoadCmds(context); } uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const @@ -1692,3 +1707,32 @@ void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& } +void ImageLoaderMachOCompressed::registerEncryption(const encryption_info_command* encryptCmd, const LinkContext& context) +{ +#if __arm__ || __arm64__ + if ( encryptCmd == NULL ) + return; + const mach_header* mh = NULL; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + mh = (mach_header*)segActualLoadAddress(i); + break; + } + } + void* start = ((uint8_t*)mh) + encryptCmd->cryptoff; + size_t len = encryptCmd->cryptsize; + uint32_t cputype = mh->cputype; + uint32_t cpusubtype = mh->cpusubtype; + uint32_t cryptid = encryptCmd->cryptid; + if (context.verboseMapping) { + dyld::log(" 0x%08lX->0x%08lX configured for FairPlay decryption\n", (long)start, (long)start+len); + } + int result = mremap_encrypted(start, len, cryptid, cputype, cpusubtype); + if ( result != 0 ) { + dyld::throwf("mremap_encrypted() => %d, errno=%d for %s\n", result, errno, this->getPath()); + } +#endif +} + + + diff --git a/src/ImageLoaderMachOCompressed.h b/src/ImageLoaderMachOCompressed.h index 1a43900..e655adc 100644 --- a/src/ImageLoaderMachOCompressed.h +++ b/src/ImageLoaderMachOCompressed.h @@ -39,10 +39,12 @@ class ImageLoaderMachOCompressed : public ImageLoaderMachO { public: static ImageLoaderMachOCompressed* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, unsigned int segCount, unsigned int libCount, const LinkContext& context); - static ImageLoaderMachOCompressed* instantiateFromFile(const char* path, int fd, const uint8_t* fileData, + static ImageLoaderMachOCompressed* instantiateFromFile(const char* path, int fd, const uint8_t *fileData, size_t lenFileData, uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, unsigned int segCount, unsigned int libCount, - const struct linkedit_data_command* codeSigCmd, const LinkContext& context); + const struct linkedit_data_command* codeSigCmd, + const struct encryption_info_command* encryptCmd, + const LinkContext& context); static ImageLoaderMachOCompressed* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, unsigned int segCount, unsigned int libCount, const LinkContext& context); static ImageLoaderMachOCompressed* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, @@ -134,7 +136,8 @@ private: static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* s); void updateOptimizedLazyPointers(const LinkContext& context); void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context); - + void registerEncryption(const struct encryption_info_command* encryptCmd, const LinkContext& context); + const struct dyld_info_command* fDyldInfo; }; diff --git a/src/dyld.cpp b/src/dyld.cpp index b34447f..648b887 100644 --- a/src/dyld.cpp +++ b/src/dyld.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include <_simple.h> #include @@ -97,11 +98,12 @@ #if DYLD_SHARED_CACHE_SUPPORT #include "dyld_cache_format.h" #endif +#include #if TARGET_IPHONE_SIMULATOR - void coresymbolication_load_image(void*, const ImageLoader*, uint64_t); - void coresymbolication_unload_image(void*, const ImageLoader*); -#else - #include "coreSymbolicationDyldSupport.hpp" + extern "C" void xcoresymbolication_load_notifier(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header); + extern "C" void xcoresymbolication_unload_notifier(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header); + #define coresymbolication_load_notifier(c, t, p, h) xcoresymbolication_load_notifier(c, t, p, h) + #define coresymbolication_unload_notifier(c, t, p, h) xcoresymbolication_unload_notifier(c, t, p, h) #endif // not libc header for send() syscall interface @@ -111,6 +113,18 @@ extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockadd // ARM and x86_64 are the only architecture that use cpu-sub-types #define CPU_SUBTYPES_SUPPORTED ((__arm__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR) +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT + #define macho_segment_command segment_command_64 + #define macho_section section_64 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 + #define macho_segment_command segment_command + #define macho_section section +#endif + #define CPU_TYPE_MASK 0x00FFFFFF /* complement of CPU_ARCH_MASK */ @@ -219,6 +233,7 @@ static cpu_subtype_t sHostCPUsubtype; #endif static ImageLoader* sMainExecutable = NULL; static bool sProcessIsRestricted = false; +static bool sProcessRequiresLibraryValidation = false; static RestrictedReason sRestrictedReason = restrictedNot; static size_t sInsertedDylibCount = 0; static std::vector sAllImages; @@ -232,8 +247,13 @@ static void* sSingleHandlers[7][3]; static void* sBatchHandlers[7][3]; static ImageLoader* sLastImageByAddressCache; static EnvironmentVariables sEnv; +#if __MAC_OS_X_VERSION_MIN_REQUIRED static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL }; +#else +static const char* sFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL }; +static const char* sLibraryFallbackPaths[] = { "/usr/local/lib", "/usr/lib", NULL }; +#endif static UndefinedHandler sUndefinedHandler = NULL; static ImageLoader* sBundleBeingLoaded = NULL; // hack until OFI is reworked #if DYLD_SHARED_CACHE_SUPPORT @@ -264,6 +284,7 @@ static bool sFrameworksFoundAsDylibs = false; static bool sHaswell = false; #endif static std::vector sDynamicReferences; +static OSSpinLock sDynamicReferencesLock = 0; static bool sLogToFile = false; static char sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries"; @@ -509,32 +530,15 @@ static OSSpinLock sAllImagesLock = 0; static void allImagesLock() { - //dyld::log("allImagesLock()\n"); -#if TARGET_IPHONE_SIMULATOR - // can't use OSSpinLockLock in simulator until thread_switch is provided by host dyld - while ( ! OSAtomicCompareAndSwapPtrBarrier((void*)0, (void*)1, (void**)&sAllImagesLock) ) { - // spin - } -#else OSSpinLockLock(&sAllImagesLock); -#endif } static void allImagesUnlock() { - //dyld::log("allImagesUnlock()\n"); -#if TARGET_IPHONE_SIMULATOR - while ( ! OSAtomicCompareAndSwapPtrBarrier((void*)1, (void*)0, (void**)&sAllImagesLock) ) { - // spin - } -#else OSSpinLockUnlock(&sAllImagesLock); -#endif } - - // utility class to assure files are closed when an exception is thrown class FileOpener { public: @@ -699,21 +703,15 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image) } } } - // mach message csdlc about dynamically loaded images + // mach message csdlc about dynamically unloaded images if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) { + uint64_t loadTimestamp = mach_absolute_time(); if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { - dyld::log("dyld core symbolication unload notification: %p %s\n", image->machHeader(), image->getPath()); + dyld::log("dyld: coresymbolication_unload_notifier(%p, 0x%016llX, %p, %s)\n", + dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->machHeader(), image->getPath()); } if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) { -#if TARGET_IPHONE_SIMULATOR - void* connection = dyld::gProcessInfo->coreSymbolicationShmPage; - if ( *((uint32_t*)connection) == 2 ) { -#else - CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld::gProcessInfo->coreSymbolicationShmPage; - if ( connection->is_valid_version() ) { -#endif - coresymbolication_unload_image(connection, image); - } + coresymbolication_unload_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->getPath(), image->machHeader()); } } } @@ -823,37 +821,22 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image } } } + if ( (state == dyld_image_state_dependents_mapped) && ((dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS) ) { + // mach message csdlc about loaded images + uint64_t loadTimestamp = mach_absolute_time(); + for (unsigned j=0; j < count; ++j) { + if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + dyld::log("dyld: coresymbolication_load_notifier(%p, 0x%016llX, %p, %s)\n", + dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageLoadAddress, infos[j].imageFilePath); + } + coresymbolication_load_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageFilePath, infos[j].imageLoadAddress); + } + } } allImagesUnlock(); if ( dontLoadReason != NULL ) throw dontLoadReason; } - if ( state == dyld_image_state_rebased ) { - if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { - for (std::vector::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::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - dyld_image_states imageState = (*it)->getState(); - if ( (imageState == state) || (orLater && (imageState > state)) ) - coresymbolication_load_image(connection, *it, load_timestamp); - } - } - } - } } @@ -925,15 +908,20 @@ static void addDynamicReference(ImageLoader* from, ImageLoader* to) { return; // don't add if this combination already exists + OSSpinLockLock(&sDynamicReferencesLock); for (std::vector::iterator it=sDynamicReferences.begin(); it != sDynamicReferences.end(); ++it) { - if ( (it->from == from) && (it->to == to) ) + if ( (it->from == from) && (it->to == to) ) { + OSSpinLockUnlock(&sDynamicReferencesLock); return; + } } + //dyld::log("addDynamicReference(%s, %s\n", from->getShortName(), to->getShortName()); ImageLoader::DynamicReference t; t.from = from; t.to = to; sDynamicReferences.push_back(t); + OSSpinLockUnlock(&sDynamicReferencesLock); } static void addImage(ImageLoader* image) @@ -1029,7 +1017,9 @@ void removeImage(ImageLoader* image) allImagesUnlock(); // remove from sDynamicReferences - sDynamicReferences.erase(std::remove_if(sDynamicReferences.begin(), sDynamicReferences.end(), RefUsesImage(image)), sDynamicReferences.end()); + OSSpinLockLock(&sDynamicReferencesLock); + sDynamicReferences.erase(std::remove_if(sDynamicReferences.begin(), sDynamicReferences.end(), RefUsesImage(image)), sDynamicReferences.end()); + OSSpinLockUnlock(&sDynamicReferencesLock); // flush find-by-address cache (do this after removed from master list, so there is no chance it can come back) if ( sLastImageByAddressCache == image ) @@ -1152,7 +1142,7 @@ static void checkDylibOverride(const char* dylibFile) //dyld::log("checkDylibOverride('%s')\n", dylibFile); uint32_t altVersion; char sysInstallName[PATH_MAX]; - if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) ) { + if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) && (sysInstallName[0] =='/') ) { //dyld::log("%s has version 0x%08X and install name %s\n", dylibFile, altVersion, sysInstallName); uint32_t sysVersion; if ( getDylibVersionAndInstallname(sysInstallName, &sysVersion, NULL) ) { @@ -1200,8 +1190,8 @@ static void checkDylibOverridesInDir(const char* dirPath) { //dyld::log("checkDylibOverridesInDir('%s')\n", dirPath); char dylibPath[PATH_MAX]; - int dirPathLen = strlen(dirPath); - strlcpy(dylibPath, dirPath, PATH_MAX); + if ( strlcpy(dylibPath, dirPath, PATH_MAX) >= PATH_MAX ) + return; DIR* dirp = opendir(dirPath); if ( dirp != NULL) { dirent entry; @@ -1211,9 +1201,9 @@ static void checkDylibOverridesInDir(const char* dirPath) break; if ( entp->d_type != DT_REG ) continue; - dylibPath[dirPathLen] = '/'; - dylibPath[dirPathLen+1] = '\0'; - if ( strlcat(dylibPath, entp->d_name, PATH_MAX) > PATH_MAX ) + if ( strlcat(dylibPath, "/", PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(dylibPath, entp->d_name, PATH_MAX) >= PATH_MAX ) continue; checkDylibOverride(dylibPath); } @@ -1226,8 +1216,8 @@ static void checkFrameworkOverridesInDir(const char* dirPath) { //dyld::log("checkFrameworkOverridesInDir('%s')\n", dirPath); char frameworkPath[PATH_MAX]; - int dirPathLen = strlen(dirPath); - strlcpy(frameworkPath, dirPath, PATH_MAX); + if ( strlcpy(frameworkPath, dirPath, PATH_MAX) >= PATH_MAX ) + return; DIR* dirp = opendir(dirPath); if ( dirp != NULL) { dirent entry; @@ -1237,18 +1227,18 @@ static void checkFrameworkOverridesInDir(const char* dirPath) break; if ( entp->d_type != DT_DIR ) continue; - frameworkPath[dirPathLen] = '/'; - frameworkPath[dirPathLen+1] = '\0'; + if ( strlcat(frameworkPath, "/", PATH_MAX) >= PATH_MAX ) + continue; int dirNameLen = strlen(entp->d_name); if ( dirNameLen < 11 ) continue; if ( strcmp(&entp->d_name[dirNameLen-10], ".framework") != 0 ) continue; - if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) > PATH_MAX ) + if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) >= PATH_MAX ) continue; - if ( strlcat(frameworkPath, "/", PATH_MAX) > PATH_MAX ) + if ( strlcat(frameworkPath, "/", PATH_MAX) >= PATH_MAX ) continue; - if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) > PATH_MAX ) + if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) >= PATH_MAX ) continue; frameworkPath[strlen(frameworkPath)-10] = '\0'; checkDylibOverride(frameworkPath); @@ -1736,16 +1726,47 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep) strlcat(sLoadingCrashMessage, ", ignoring DYLD_* env vars", sizeof(sLoadingCrashMessage)); } +static void defaultUninitializedFallbackPaths(const char* envp[]) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment + const char* home = _simple_getenv(envp, "HOME");; + if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { + const char** fpaths = sFrameworkFallbackPaths; + if ( home == NULL ) + removePathWithPrefix(fpaths, "$HOME"); + else + paths_expand_roots(fpaths, "$HOME", home); + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = fpaths; + } -static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) + // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment + if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) { + const char** lpaths = sLibraryFallbackPaths; + if ( home == NULL ) + removePathWithPrefix(lpaths, "$HOME"); + else + paths_expand_roots(lpaths, "$HOME", home); + sEnv.DYLD_FALLBACK_LIBRARY_PATH = lpaths; + } +#else + if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sFrameworkFallbackPaths; + + if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) + sEnv.DYLD_FALLBACK_LIBRARY_PATH = sLibraryFallbackPaths; +#endif +} + + +static void checkEnvironmentVariables(const char* envp[]) { - const char* home = NULL; const char** p; for(p = envp; *p != NULL; p++) { const char* keyEqualsValue = *p; if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) { const char* equals = strchr(keyEqualsValue, '='); - if ( (equals != NULL) && !ignoreEnviron ) { + if ( equals != NULL ) { strlcat(sLoadingCrashMessage, "\n", sizeof(sLoadingCrashMessage)); strlcat(sLoadingCrashMessage, keyEqualsValue, sizeof(sLoadingCrashMessage)); const char* value = &equals[1]; @@ -1756,9 +1777,6 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) processDyldEnvironmentVariable(key, value, NULL); } } - else if ( strncmp(keyEqualsValue, "HOME=", 5) == 0 ) { - home = &keyEqualsValue[5]; - } else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) { const char* path = &keyEqualsValue[16]; sEnv.LD_LIBRARY_PATH = parseColonList(path, NULL); @@ -1769,39 +1787,43 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) checkLoadCommandEnvironmentVariables(); #endif // SUPPORT_LC_DYLD_ENVIRONMENT - // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment - if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { - const char** paths = sFrameworkFallbackPaths; - if ( home == NULL ) - removePathWithPrefix(paths, "$HOME"); - else - paths_expand_roots(paths, "$HOME", home); - sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = paths; - } - - // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment - if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) { - const char** paths = sLibraryFallbackPaths; - if ( home == NULL ) - removePathWithPrefix(paths, "$HOME"); - else - paths_expand_roots(paths, "$HOME", home); - sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths; - } - // 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__ @@ -1829,6 +1851,22 @@ static void getHostInfo() sHostCPU = info.cpu_type; sHostCPUsubtype = info.cpu_subtype; mach_port_deallocate(mach_task_self(), hostPort); + #if __x86_64__ + #if TARGET_IPHONE_SIMULATOR + sHaswell = false; + #else + sHaswell = (sHostCPUsubtype == CPU_SUBTYPE_X86_64_H); + // x86_64h: Fall back to the x86_64 slice if an app requires GC. + if ( sHaswell ) { + if ( isGCProgram(mainExecutableMH, mainExecutableSlide) ) { + // When running a GC program on a haswell machine, don't use and 'h slices + sHostCPUsubtype = CPU_SUBTYPE_X86_64_ALL; + sHaswell = false; + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + } + } + #endif + #endif #endif #endif } @@ -2389,11 +2427,13 @@ static bool isSimulatorBinary(const uint8_t* firstPage, const char* path) for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: return true; case LC_VERSION_MIN_MACOSX: // grandfather in a few libSystem dylibs - if ( strncmp(path, "/usr/lib/system/libsystem_", 26) == 0 ) - return true; + if (strstr(path, "/usr/lib/system") || strstr(path, "/usr/lib/libSystem")) + return true; return false; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); @@ -2457,12 +2497,16 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* default: throw "mach-o, but wrong filetype"; } - + #if TARGET_IPHONE_SIMULATOR + #if TARGET_OS_WATCH || TARGET_OS_TV + // disable error during bring up of these simulators + #else // dyld_sim should restrict loading osx binaries if ( !isSimulatorBinary(firstPage, path) ) { throw "mach-o, but not built for iOS simulator"; } + #endif #endif // instantiate an image @@ -2738,7 +2782,7 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load ImageLoader* image = NULL; if ( strncmp(path, "@executable_path/", 17) == 0 ) { // executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305 - if ( sProcessIsRestricted ) + if ( sProcessIsRestricted && !sProcessRequiresLibraryValidation ) throwf("unsafe use of @executable_path in %s with restricted binary", context.origin); // handle @executable_path path prefix const char* executablePath = sExecPath; @@ -2770,7 +2814,7 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load } else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) { // @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305 - if ( sProcessIsRestricted && (strcmp(context.origin, sExecPath) == 0) ) + if ( sProcessIsRestricted && (strcmp(context.origin, sExecPath) == 0) && !sProcessRequiresLibraryValidation ) throwf("unsafe use of @loader_path in %s with restricted binary", context.origin); // handle @loader_path path prefix char newPath[strlen(context.origin) + strlen(path)]; @@ -2834,7 +2878,7 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load if ( (exceptions != NULL) && (trailingPath != path) ) return NULL; } - else if (sProcessIsRestricted && (path[0] != '/' )) { + else if (sProcessIsRestricted && (path[0] != '/' ) && !sProcessRequiresLibraryValidation) { throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin); } @@ -3196,6 +3240,23 @@ const void* imMemorySharedCacheHeader() return sSharedCache; } +const char* getStandardSharedCacheFilePath() +{ +#if __IPHONE_OS_VERSION_MIN_REQUIRED + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME; +#else + #if __x86_64__ + if ( sHaswell ) { + const char* path2 = MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H; + struct stat statBuf; + if ( my_stat(path2, &statBuf) == 0 ) + return path2; + } + #endif + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME; +#endif +} + int openSharedCacheFile() { char path[MAXPATHLEN]; @@ -3563,7 +3624,8 @@ static void mapSharedCache() // zero size in header means signature runs to end-of-file if ( signatureSize == 0 ) { struct stat stat_buf; - if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 ) + // FIXME: need size of cache file actually used + if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 ) signatureSize = stat_buf.st_size - header->codeSignatureOffset; } if ( signatureSize != 0 ) { @@ -4021,16 +4083,6 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha } -#if __LP64__ - #define LC_SEGMENT_COMMAND LC_SEGMENT_64 - #define macho_segment_command segment_command_64 - #define macho_section section_64 -#else - #define LC_SEGMENT_COMMAND LC_SEGMENT - #define macho_segment_command segment_command - #define macho_section section -#endif - // // Look for a special segment in the mach header. @@ -4066,20 +4118,18 @@ static bool hasRestrictedSegment(const macho_header* mh) return false; } + #if SUPPORT_VERSIONED_PATHS -// -// Peeks at a dylib file and returns its current_version and install_name. -// Returns false on error. -// -static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName) + +static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) { + firstPage[0] = 0; // open file (automagically closed when this function exits) FileOpener file(dylibPath); - + if ( file.getFileDescriptor() == -1 ) return false; - uint8_t firstPage[4096]; if ( pread(file.getFileDescriptor(), firstPage, 4096, 0) != 4096 ) return false; @@ -4096,9 +4146,33 @@ static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* versi return false; } } + + return true; +} + +// +// Peeks at a dylib file and returns its current_version and install_name. +// Returns false on error. +// +static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName) +{ + uint8_t firstPage[4096]; + const macho_header* mh = (macho_header*)firstPage; + if ( !readFirstPage(dylibPath, firstPage) ) { + #if DYLD_SHARED_CACHE_SUPPORT + // If file cannot be read, check to see if path is in shared cache + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( !findInSharedCacheImage(dylibPath, true, NULL, &mhInCache, &pathInCache, &slideInCache) ) + return false; + mh = mhInCache; + #else + return false; + #endif + } // check mach-o header - const mach_header* mh = (mach_header*)firstPage; if ( mh->magic != sMainExecutableMachHeader->magic ) return false; if ( mh->cputype != sMainExecutableMachHeader->cputype ) @@ -4212,13 +4286,16 @@ void garbageCollectImages() for (std::vector::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::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { ImageLoader* image = *it; @@ -4226,11 +4303,11 @@ void garbageCollectImages() deadImages[i++] = image; if (gLogAPIs) dyld::log("dlclose(), found unused image %p %s\n", image, image->getShortName()); ++deadCount; + maxRangeCount += image->segmentCount(); } } // collect phase: run termination routines for images not marked in-use - const int maxRangeCount = deadCount*2; __cxa_range_t ranges[maxRangeCount]; int rangeCount = 0; for (unsigned i=0; i < deadCount; ++i) { @@ -4309,25 +4386,6 @@ void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths) preflight_finally(image); } -#if __x86_64__ -static bool isHaswell() -{ -#if TARGET_IPHONE_SIMULATOR - return false; -#else - // check system is capable of running x86_64h code - struct host_basic_info info; - mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; - mach_port_t hostPort = mach_host_self(); - kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); - mach_port_deallocate(mach_task_self(), hostPort); - if ( result != KERN_SUCCESS ) - return false; - return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H ); -#endif -} -#endif - static void loadInsertedDylib(const char* path) { ImageLoader* image = NULL; @@ -4358,24 +4416,34 @@ static void loadInsertedDylib(const char* path) } } -static bool processRestricted(const macho_header* mainExecutableMH) +static bool processRestricted(const macho_header* mainExecutableMH, bool* ignoreEnvVars, bool* processRequiresLibraryValidation) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if TARGET_IPHONE_SIMULATOR + gLinkContext.codeSigningEnforced = true; +#else // ask kernel if code signature of program makes it restricted uint32_t flags; if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { + if (flags & CS_REQUIRE_LV) + *processRequiresLibraryValidation = true; + + #if __MAC_OS_X_VERSION_MIN_REQUIRED if ( flags & CS_ENFORCEMENT ) { gLinkContext.codeSigningEnforced = true; } + if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0) ) { + sRestrictedReason = restrictedByEntitlements; + return true; + } + #else + if ((flags & CS_ENFORCEMENT) && !(flags & CS_GET_TASK_ALLOW)) { + *ignoreEnvVars = true; + } + gLinkContext.codeSigningEnforced = true; + #endif } - if (flags & CS_RESTRICT) { - sRestrictedReason = restrictedByEntitlements; - return true; - } -#else - gLinkContext.codeSigningEnforced = true; #endif - + // all processes with setuid or setgid bit set are restricted if ( issetugid() ) { sRestrictedReason = restrictedBySetGUid; @@ -4426,7 +4494,7 @@ typedef int (*fcntl_proc_t)(int, int, void*); typedef int (*ioctl_proc_t)(int, unsigned long, void*); static void* getProcessInfo() { return dyld::gProcessInfo; } static SyscallHelpers sSysCalls = { - 3, + 4, // added in version 1 (open_proc_t)&open, &close, @@ -4462,7 +4530,10 @@ static SyscallHelpers sSysCalls = { // added in version 3 &opendir, &readdir_r, - &closedir + &closedir, + // added in version 4 + &coresymbolication_load_notifier, + &coresymbolication_unload_notifier }; __attribute__((noinline)) @@ -4498,32 +4569,73 @@ static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH, // calculate total size of dyld segments const macho_header* mh = (const macho_header*)firstPage; + struct macho_segment_command* lastSeg = NULL; + struct macho_segment_command* firstSeg = NULL; uintptr_t mappingSize = 0; uintptr_t preferredLoadAddress = 0; const uint32_t cmd_count = mh->ncmds; + if ( (sizeof(macho_header) + mh->sizeofcmds) > 4096 ) + return 0; const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* const endCmds = (struct load_command*)(((char*)mh) + sizeof(macho_header) + mh->sizeofcmds); const struct load_command* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize; + if ( cmdLength < 8 ) + return 0; + const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); + if ( (nextCmd > endCmds) || (nextCmd < cmd) ) + return 0; switch (cmd->cmd) { case LC_SEGMENT_COMMAND: { struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - mappingSize += seg->vmsize; - if ( seg->fileoff == 0 ) + if ( seg->vmaddr + seg->vmsize < seg->vmaddr ) + return 0; + if ( seg->vmsize < seg->filesize ) + return 0; + if ( lastSeg == NULL ) { + // first segment must be __TEXT and start at beginning of file/slice + firstSeg = seg; + if ( strcmp(seg->segname, "__TEXT") != 0 ) + return 0; + if ( seg->fileoff != 0 ) + return 0; + if ( seg->filesize < (sizeof(macho_header) + mh->sizeofcmds) ) + return 0; preferredLoadAddress = seg->vmaddr; + } + else { + // other sements must be continguous with previous segment and not executable + if ( lastSeg->fileoff + lastSeg->filesize != seg->fileoff ) + return 0; + if ( lastSeg->vmaddr + lastSeg->vmsize != seg->vmaddr ) + return 0; + if ( (seg->initprot & VM_PROT_EXECUTE) != 0 ) + return 0; + } + mappingSize += seg->vmsize; + lastSeg = seg; } break; + case LC_SEGMENT_COMMAND_WRONG: + return 0; } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + cmd = nextCmd; } + // last segment must be named __LINKEDIT and not writable + if ( strcmp(lastSeg->segname, "__LINKEDIT") != 0 ) + return 0; + if ( lastSeg->initprot & VM_PROT_WRITE ) + return 0; // reserve space, then mmap each segment vm_address_t loadAddress = 0; - uintptr_t entry = 0; if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 ) return 0; cmd = cmds; struct linkedit_data_command* codeSigCmd = NULL; + struct source_version_command* dyldVersionCmd = NULL; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_SEGMENT_COMMAND: @@ -4536,26 +4648,23 @@ static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH, return 0; } break; - case LC_UNIXTHREAD: - { - #if __i386__ - const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); - entry = (registers->__eip + loadAddress - preferredLoadAddress); - #elif __x86_64__ - const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); - entry = (registers->__rip + loadAddress - preferredLoadAddress); - #endif - } - break; case LC_CODE_SIGNATURE: codeSigCmd = (struct linkedit_data_command*)cmd; break; + case LC_SOURCE_VERSION: + dyldVersionCmd = (struct source_version_command*)cmd; + break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - + + // must have code signature which is contained within LINKEDIT segment if ( codeSigCmd == NULL ) return 0; + if ( codeSigCmd->dataoff < lastSeg->fileoff ) + return 0; + if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) ) + return 0; fsignatures_t siginfo; siginfo.fs_file_start=fileOffset; // start of mach-o slice in fat file @@ -4566,8 +4675,37 @@ static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH, dyld::log("fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d\n", errno); return 0; } - close(fd); + // file range covered by code signature must extend up to code signature itself + if ( siginfo.fs_file_start < codeSigCmd->dataoff ) + return 0; + + // walk newly mapped dyld_sim __TEXT load commands to find entry point + uintptr_t entry = 0; + cmd = (struct load_command*)(((char*)loadAddress)+sizeof(macho_header)); + const uint32_t count = ((macho_header*)(loadAddress))->ncmds; + for (uint32_t i = 0; i < count; ++i) { + if (cmd->cmd == LC_UNIXTHREAD) { + #if __i386__ + const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); + // entry point must be in first segment + if ( registers->__eip < firstSeg->vmaddr ) + return 0; + if ( registers->__eip > (firstSeg->vmaddr + firstSeg->vmsize) ) + return 0; + entry = (registers->__eip + loadAddress - preferredLoadAddress); + #elif __x86_64__ + const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); + // entry point must be in first segment + if ( registers->__rip < firstSeg->vmaddr ) + return 0; + if ( registers->__rip > (firstSeg->vmaddr + firstSeg->vmsize) ) + return 0; + entry = (registers->__rip + loadAddress - preferredLoadAddress); + #endif + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } // notify debugger that dyld_sim is loaded dyld_image_info info; @@ -4576,13 +4714,14 @@ static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH, info.imageFileModDate = sb.st_mtime; addImagesToAllImages(1, &info); dyld::gProcessInfo->notification(dyld_image_adding, 1, &info); - + + const char** appleParams = apple; // jump into new simulator dyld typedef uintptr_t (*sim_entry_proc_t)(int argc, const char* argv[], const char* envp[], const char* apple[], const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, const dyld::SyscallHelpers* vtable, uintptr_t* startGlue); sim_entry_proc_t newDyld = (sim_entry_proc_t)entry; - return (*newDyld)(argc, argv, envp, apple, mainExecutableMH, (macho_header*)loadAddress, + return (*newDyld)(argc, argv, envp, appleParams, mainExecutableMH, (macho_header*)loadAddress, loadAddress - preferredLoadAddress, &sSysCalls, startGlue); } @@ -4649,7 +4788,6 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, // Remove interim apple[0] transition code from dyld if (!sExecPath) sExecPath = apple[0]; - sExecPath = apple[0]; bool ignoreEnvironmentVariables = false; if ( sExecPath[0] != '/' ) { // have relative path, use cwd to make absolute @@ -4669,25 +4807,25 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, ++sExecShortName; else sExecShortName = sExecPath; - sProcessIsRestricted = processRestricted(mainExecutableMH); + sProcessIsRestricted = processRestricted(mainExecutableMH, &ignoreEnvironmentVariables, &sProcessRequiresLibraryValidation); if ( sProcessIsRestricted ) { #if SUPPORT_LC_DYLD_ENVIRONMENT checkLoadCommandEnvironmentVariables(); -#if SUPPORT_VERSIONED_PATHS - checkVersionedPaths(); -#endif #endif pruneEnvironmentVariables(envp, &apple); // set again because envp and apple may have changed or moved setContext(mainExecutableMH, argc, argv, envp, apple); } - else - checkEnvironmentVariables(envp, ignoreEnvironmentVariables); - if ( sEnv.DYLD_PRINT_OPTS ) + else { + if ( !ignoreEnvironmentVariables ) + checkEnvironmentVariables(envp); + defaultUninitializedFallbackPaths(envp); + } + if ( sEnv.DYLD_PRINT_OPTS ) printOptions(argv); if ( sEnv.DYLD_PRINT_ENV ) printEnvironmentVariables(envp); - getHostInfo(); + getHostInfo(mainExecutableMH, mainExecutableSlide); // install gdb notifier stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB); stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages); @@ -4713,9 +4851,13 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); gLinkContext.mainExecutable = sMainExecutable; gLinkContext.processIsRestricted = sProcessIsRestricted; + gLinkContext.processRequiresLibraryValidation = sProcessRequiresLibraryValidation; gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH); #if TARGET_IPHONE_SIMULATOR + #if TARGET_OS_WATCH || TARGET_OS_TV + // disable error during bring up of these simulators + #else // check main executable is not too new for this OS { if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) { @@ -4731,17 +4873,21 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); } } + #endif #endif // load shared cache - #if __x86_64__ - sHaswell = isHaswell(); - #endif checkSharedRegionDisable(); #if DYLD_SHARED_CACHE_SUPPORT if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) mapSharedCache(); #endif + + // Now that shared cache is loaded, setup an versioned dylib overrides + #if SUPPORT_VERSIONED_PATHS + checkVersionedPaths(); + #endif + // load any inserted libraries if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) { for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) @@ -4776,6 +4922,15 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, image->registerInterposing(); } } + + // 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); diff --git a/src/dyld.h b/src/dyld.h index 3cdc3c7..fe6c9b6 100644 --- a/src/dyld.h +++ b/src/dyld.h @@ -114,6 +114,7 @@ namespace dyld { extern void logBindings(const char* format, ...); #endif extern bool processIsRestricted(); + extern const char* getStandardSharedCacheFilePath(); extern int my_stat(const char* path, struct stat* buf); extern int my_open(const char* path, int flag, int other); } diff --git a/src/dyldAPIs.cpp b/src/dyldAPIs.cpp index 2c888ce..b64331a 100644 --- a/src/dyldAPIs.cpp +++ b/src/dyldAPIs.cpp @@ -147,10 +147,13 @@ static struct dyld_func dyld_funcs[] = { {"__dyld_shared_cache_some_image_overridden", (void*)dyld_shared_cache_some_image_overridden }, {"__dyld_process_is_restricted", (void*)dyld::processIsRestricted }, {"__dyld_dynamic_interpose", (void*)dyld_dynamic_interpose }, +#if DYLD_SHARED_CACHE_SUPPORT + {"__dyld_shared_cache_file_path", (void*)dyld::getStandardSharedCacheFilePath }, +#endif + {"__dyld_get_image_header_containing_address", (void*)dyld_image_header_containing_address }, // deprecated #if DEPRECATED_APIS_SUPPORTED - {"__dyld_get_image_header_containing_address", (void*)_dyld_get_image_header_containing_address }, {"__dyld_lookup_and_bind", (void*)client_dyld_lookup_and_bind }, {"__dyld_lookup_and_bind_with_hint", (void*)_dyld_lookup_and_bind_with_hint }, {"__dyld_lookup_and_bind_fully", (void*)_dyld_lookup_and_bind_fully }, @@ -341,7 +344,7 @@ const char* _dyld_get_image_name(uint32_t image_index) return NULL; } -const struct mach_header * _dyld_get_image_header_containing_address(const void* address) +const struct mach_header * dyld_image_header_containing_address(const void* address) { if ( dyld::gLogAPIs ) dyld::log("%s(%p)\n", __func__, address); @@ -1115,7 +1118,7 @@ bool NSUnLinkModule(NSModule module, uint32_t options) return false; dyld::runImageStaticTerminators(image); if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 13) ) { - __cxa_range_t ranges[3]; + __cxa_range_t ranges[image->segmentCount()]; int rangeCount = 0; for (unsigned int j=0; j < image->segmentCount(); ++j) { if ( !image->segExecutable(j) ) @@ -1417,15 +1420,16 @@ void* dlopen(const char* path, int mode) void* result = NULL; ImageLoader* image = NULL; std::vector 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); diff --git a/src/dyldAPIsInLibSystem.cpp b/src/dyldAPIsInLibSystem.cpp index 6ded1a7..d1e48f5 100644 --- a/src/dyldAPIsInLibSystem.cpp +++ b/src/dyldAPIsInLibSystem.cpp @@ -58,6 +58,14 @@ extern "C" void __cxa_finalize_ranges(const struct __cxa_range_t ranges[], int c #define LC_VERSION_MIN_IPHONEOS 0x25 #endif +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + #ifndef LC_LOAD_UPWARD_DYLIB #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ @@ -412,19 +420,46 @@ int32_t NSVersionOfRunTimeLibrary(const char* libraryName) return (-1); } + #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff)) -/* - * Returns the sdk version (encode as nibble XXXX.YY.ZZ) the - * specified binary was built against. - * - * First looks for LC_VERSION_MIN_MACOSX/LC_VERSION_MIN_IPHONEOS - * in binary and if sdk field is not zero, return that value. - * Otherwise, looks for the libSystem.B.dylib the binary linked - * against and uses a table to convert that to an sdk version. - */ -uint32_t dyld_get_sdk_version(const mach_header* mh) +static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* loadCommand, uint32_t* minOS, uint32_t* sdk) +{ + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return false; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return 0; + } + const version_min_command* versCmd; + switch ( cmd->cmd ) { + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + *loadCommand = versCmd->cmd; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + return true; + } + cmd = nextCmd; + } + return false; +} + +#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED +static uint32_t deriveSDKVersFromDylibs(const mach_header* mh) { const load_command* startCmds = NULL; if ( mh->magic == MH_MAGIC_64 ) @@ -435,15 +470,14 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) return 0; // not a mach-o file, or wrong endianness const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); - const version_min_command* versCmd; const dylib_command* dylibCmd; const load_command* cmd = startCmds; const char* dylibName; -#if __IPHONE_OS_VERSION_MIN_REQUIRED + #if __IPHONE_OS_VERSION_MIN_REQUIRED uint32_t foundationVers = 0; -#else + #else uint32_t libSystemVers = 0; -#endif + #endif for(uint32_t i = 0; i < mh->ncmds; ++i) { const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); // sanity check size of command @@ -451,20 +485,6 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) return 0; } switch ( cmd->cmd ) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED - case LC_VERSION_MIN_IPHONEOS: -#else - case LC_VERSION_MIN_MACOSX: -#endif - versCmd = (version_min_command*)cmd; -#ifdef DICE_KIND_DATA - if ( versCmd->sdk != 0 ) - return versCmd->sdk; // found explicit SDK version -#else - if ( versCmd->reserved != 0 ) - return versCmd->reserved; // found explicit SDK version -#endif - break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_LOAD_UPWARD_DYLIB: @@ -473,13 +493,13 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) return 0; dylibName = (char*)dylibCmd + dylibCmd->dylib.name.offset; -#if __IPHONE_OS_VERSION_MIN_REQUIRED + #if __IPHONE_OS_VERSION_MIN_REQUIRED if ( strcmp(dylibName, "/System/Library/Frameworks/Foundation.framework/Foundation") == 0 ) foundationVers = dylibCmd->dylib.current_version; -#else + #else if ( strcmp(dylibName, "/usr/lib/libSystem.B.dylib") == 0 ) libSystemVers = dylibCmd->dylib.current_version; -#endif + #endif break; } cmd = nextCmd; @@ -490,7 +510,7 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) uint32_t osVersion; }; -#if __IPHONE_OS_VERSION_MIN_REQUIRED + #if __IPHONE_OS_VERSION_MIN_REQUIRED static const DylibToOSMapping foundationMapping[] = { { PACKED_VERSION(678,24,0), DYLD_IOS_VERSION_2_0 }, { PACKED_VERSION(678,26,0), DYLD_IOS_VERSION_2_1 }, @@ -506,8 +526,10 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) { PACKED_VERSION(890,1,0), DYLD_IOS_VERSION_5_1 }, { PACKED_VERSION(992,0,0), DYLD_IOS_VERSION_6_0 }, { PACKED_VERSION(993,0,0), DYLD_IOS_VERSION_6_1 }, - { PACKED_VERSION(1038,14,0),DYLD_IOS_VERSION_7_0 }, // check final - { PACKED_VERSION(0,0,0), DYLD_IOS_VERSION_7_0 } + { PACKED_VERSION(1038,14,0),DYLD_IOS_VERSION_7_0 }, + { PACKED_VERSION(0,0,0), DYLD_IOS_VERSION_7_0 } + // We don't need to expand this table because all recent + // binaries have LC_VERSION_MIN_ load command. }; if ( foundationVers != 0 ) { @@ -521,7 +543,7 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) } } -#else + #else // Note: versions are for the GM release. The last entry should // always be zero. At the start of the next major version, // a new last entry needs to be added and the previous zero @@ -534,6 +556,8 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) { PACKED_VERSION(169,3,0), DYLD_MACOSX_VERSION_10_8 }, { PACKED_VERSION(1197,0,0), DYLD_MACOSX_VERSION_10_9 }, { PACKED_VERSION(0,0,0), DYLD_MACOSX_VERSION_10_9 } + // We don't need to expand this table because all recent + // binaries have LC_VERSION_MIN_ load command. }; if ( libSystemVers != 0 ) { @@ -546,9 +570,82 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) lastOsVersion = p->osVersion; } } + #endif + return 0; +} #endif - + + +#if __WATCH_OS_VERSION_MIN_REQUIRED +static uint32_t watchVersToIOSVers(uint32_t vers) +{ + return vers + 0x00070000; +} + +uint32_t dyld_get_program_sdk_watch_os_version() +{ + const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader(); + uint32_t loadCommand; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) { + if ( loadCommand == LC_VERSION_MIN_WATCHOS ) + return sdk; + } + return 0; +} +#endif + +/* + * Returns the sdk version (encode as nibble XXXX.YY.ZZ) the + * specified binary was built against. + * + * First looks for LC_VERSION_MIN_* in binary and if sdk field is + * not zero, return that value. + * Otherwise, looks for the libSystem.B.dylib the binary linked + * against and uses a table to convert that to an sdk version. + */ +uint32_t dyld_get_sdk_version(const mach_header* mh) +{ + uint32_t loadCommand; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) { + switch (loadCommand) { +#if __WATCH_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_WATCHOS: + // new binary. sdk version looks like "2.0" but API wants "9.0" + return watchVersToIOSVers(sdk); + case LC_VERSION_MIN_IPHONEOS: + // old binary. sdk matches API semantics so can return directly. + return sdk; +#elif __TV_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_IPHONEOS: + return sdk; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_IPHONEOS: + if ( sdk != 0 ) // old binaries might not have SDK set + return sdk; + break; +#else + case LC_VERSION_MIN_MACOSX: + if ( sdk != 0 ) // old binaries might not have SDK set + return sdk; + break; +#endif + } + } + +#if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED + // All WatchOS and tv OS binaries should have version load command. return 0; +#else + // MacOSX and iOS have old binaries without version load commmand. + return deriveSDKVersFromDylibs(mh); +#endif } uint32_t dyld_get_program_sdk_version() @@ -558,34 +655,31 @@ uint32_t dyld_get_program_sdk_version() uint32_t dyld_get_min_os_version(const struct mach_header* mh) { - const load_command* startCmds = NULL; - if ( mh->magic == MH_MAGIC_64 ) - startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); - else if ( mh->magic == MH_MAGIC ) - startCmds = (load_command*)((char *)mh + sizeof(mach_header)); - else - return 0; // not a mach-o file, or wrong endianness - - const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); - const version_min_command* versCmd; - const load_command* cmd = startCmds; - for(uint32_t i = 0; i < mh->ncmds; ++i) { - const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); - // sanity check size of command - if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { - return 0; - } - switch ( cmd->cmd ) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED + uint32_t loadCommand; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &loadCommand, &minOS, &sdk) ) { + switch (loadCommand) { +#if __WATCH_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_WATCHOS: + // new binary. OS version looks like "2.0" but API wants "9.0" + return watchVersToIOSVers(minOS); + case LC_VERSION_MIN_IPHONEOS: + // old binary. OS matches API semantics so can return directly. + return minOS; +#elif __TV_OS_VERSION_MIN_REQUIRED + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_IPHONEOS: + return minOS; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED case LC_VERSION_MIN_IPHONEOS: + return minOS; #else case LC_VERSION_MIN_MACOSX: + return minOS; #endif - versCmd = (version_min_command*)cmd; - return versCmd->version; // found explicit min OS version - break; } - cmd = nextCmd; } return 0; } @@ -1448,6 +1542,17 @@ const char* dyld_image_path_containing_address(const void* addr) return p(addr); } +const struct mach_header* dyld_image_header_containing_address(const void* addr) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static const mach_header* (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_header_containing_address", (void**)&p); + return p(addr); +} + + bool dyld_shared_cache_some_image_overridden() { DYLD_NO_LOCK_THIS_BLOCK; @@ -1469,6 +1574,17 @@ bool dyld_process_is_restricted() return p(); } +#if DYLD_SHARED_CACHE_SUPPORT +const char* dyld_shared_cache_file_path() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static const char* (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_shared_cache_file_path", (void**)&p); + return p(); +} +#endif void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) { diff --git a/src/dyldNew.cpp b/src/dyldNew.cpp index d68fc4e..ebccc77 100644 --- a/src/dyldNew.cpp +++ b/src/dyldNew.cpp @@ -68,6 +68,10 @@ void* malloc(size_t size) return p; } else { + if ( size > DYLD_POOL_CHUNK_SIZE ) { + dyld::log("dyld malloc overflow: size=%zu\n", size); + exit(1); + } size = (size+sizeof(void*)-1) & (-sizeof(void*)); // pointer align uint8_t* result = currentPool->current; currentPool->current += size; @@ -128,7 +132,13 @@ void* calloc(size_t count, size_t size) return result; } else { - return malloc(count*size); + // Check for overflow of integer multiplication + size_t total = count * size; + if ( total/count != size ) { + dyld::log("dyld calloc overflow: count=%zu, size=%zu\n", count, size); + exit(1); + } + return malloc(total); } } diff --git a/src/dyldStartup.s b/src/dyldStartup.s index 58055cb..71d45a3 100644 --- a/src/dyldStartup.s +++ b/src/dyldStartup.s @@ -217,7 +217,7 @@ ___dso_handle: .long 0 __dyld_start: mov r8, sp // save stack pointer sub sp, #16 // make room for outgoing parameters - bic sp, sp, #7 // force 8-byte alignment + bic sp, sp, #15 // force 16-byte alignment // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) diff --git a/src/dyldSyscallInterface.h b/src/dyldSyscallInterface.h index 12be7db..01a8e80 100644 --- a/src/dyldSyscallInterface.h +++ b/src/dyldSyscallInterface.h @@ -27,6 +27,7 @@ #define __DYLD_SYSCALL_HELPERS__ #include +#include #if __cplusplus namespace dyld { @@ -74,6 +75,9 @@ namespace dyld { DIR* (*opendir)(const char* path); int (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result); int (*closedir)(DIR* dirp); + // Added in version 4 + void (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header); + void (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header); }; extern const struct SyscallHelpers* gSyscallHelpers; diff --git a/src/dyld_stub_binder.s b/src/dyld_stub_binder.s index c4c45ef..5cc5658 100644 --- a/src/dyld_stub_binder.s +++ b/src/dyld_stub_binder.s @@ -270,12 +270,14 @@ dyld_stub_binder: #if __ARM_ARCH_7K__ vpush {d0, d1, d2, d3, d4, d5, d6, d7} + sub sp, sp, #8 // Align stack to 16 bytes. #endif // call dyld::fastBindLazySymbol(loadercache, lazyinfo) bl __Z21_dyld_fast_stub_entryPvl mov ip, r0 // move the symbol`s address into ip #if __ARM_ARCH_7K__ + add sp, sp, #8 vpop {d0, d1, d2, d3, d4, d5, d6, d7} #endif diff --git a/src/glue.c b/src/glue.c index 5616c87..bc7741f 100644 --- a/src/glue.c +++ b/src/glue.c @@ -46,6 +46,7 @@ #include "dyld_images.h" #include #include + #include #if __LP64__ #define LC_SEGMENT_COMMAND LC_SEGMENT_64 typedef struct segment_command_64 macho_segment_command; @@ -339,6 +340,9 @@ int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other) // #if TARGET_IPHONE_SIMULATOR + +#include + int myopen(const char* path, int oflag, int extra) __asm("_open"); int myopen(const char* path, int oflag, int extra) { return gSyscallHelpers->open(path, oflag, extra); @@ -463,6 +467,13 @@ uint64_t mach_absolute_time(void) { return gSyscallHelpers->mach_absolute_time(); } +kern_return_t thread_switch(mach_port_name_t thread_name, + int option, mach_msg_timeout_t option_time) { + if ( gSyscallHelpers->version < 2 ) + return KERN_FAILURE; + return gSyscallHelpers->thread_switch(thread_name, option, option_time); +} + DIR* opendir(const char* path) { if ( gSyscallHelpers->version < 3 ) return NULL; @@ -481,15 +492,61 @@ int closedir(DIR* dirp) { return gSyscallHelpers->closedir(dirp); } +#define SUPPORT_HOST_10_10 1 + +#if SUPPORT_HOST_10_10 +typedef int (*FuncPtr_nanosleep)(const struct timespec*, struct timespec*); +typedef kern_return_t (*FuncPtr_mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*); +typedef mach_msg_return_t (*FuncPtr_mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t); +typedef int (*FuncPtr_kill)(pid_t pid, int sig); +typedef pid_t (*FuncPtr_getpid)(); +typedef bool (*FuncPtr_OSAtomicCompareAndSwap32)(int32_t, int32_t, volatile int32_t*); -typedef void (*LoadFuncPtr)(void* shm, void* image, uint64_t timestamp); -typedef void (*UnloadFuncPtr)(void* shm, void* image); +static FuncPtr_nanosleep proc_nanosleep = NULL; +static FuncPtr_mach_port_allocate proc_mach_port_allocate = NULL; +static FuncPtr_mach_msg proc_mach_msg = NULL; +static FuncPtr_kill proc_kill = NULL; +static FuncPtr_getpid proc_getpid = NULL; +static FuncPtr_OSAtomicCompareAndSwap32 proc_OSAtomicCompareAndSwap32 = NULL; + + +int nanosleep(const struct timespec* p1, struct timespec* p2) +{ + return (*proc_nanosleep)(p1, p2); +} -static LoadFuncPtr sLoadPtr = NULL; -static UnloadFuncPtr sUnloadPtr = NULL; +kern_return_t mach_port_allocate(ipc_space_t p1, mach_port_right_t p2, mach_port_name_t* p3) +{ + return (*proc_mach_port_allocate)(p1, p2, p3); +} + +mach_msg_return_t mach_msg(mach_msg_header_t* p1, mach_msg_option_t p2, mach_msg_size_t p3, mach_msg_size_t p4, mach_port_name_t p5, mach_msg_timeout_t p6, mach_port_name_t p7) +{ + return (*proc_mach_msg)(p1, p2, p3, p4, p5, p6, p7); +} + +int kill(pid_t p1, int p2) +{ + return (*proc_kill)(p1, p2); +} + +pid_t getpid() +{ + return (*proc_getpid)(); +} + +bool OSAtomicCompareAndSwap32(int32_t p1, int32_t p2, volatile int32_t* p3) +{ + return (*proc_OSAtomicCompareAndSwap32)(p1, p2, p3); +} + + +// Look up sycalls in host dyld needed by coresymbolication_ routines in dyld_sim +static void findHostFunctions() { + // Only look up symbols once + if ( proc_nanosleep != NULL ) + return; -// Lookup of coresymbolication functions in host dyld. -static void findCSProcs() { struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo()); const struct mach_header* hostDyldMH = imageInfo->dyldImageLoadAddress; @@ -539,30 +596,45 @@ static void findCSProcs() { for (const macho_nlist* s = localsStart; s < localsEnd; ++s) { if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { const char* name = &symbolTableStrings[s->n_un.n_strx]; - if ( strcmp(name, "__Z28coresymbolication_load_imageP25CSCppDyldSharedMemoryPagePK11ImageLoadery") == 0 ) - sLoadPtr = (LoadFuncPtr)(s->n_value + slide); - else if ( strcmp(name, "__Z30coresymbolication_unload_imageP25CSCppDyldSharedMemoryPagePK11ImageLoader") == 0 ) - sUnloadPtr = (UnloadFuncPtr)(s->n_value + slide); + if ( strcmp(name, "_nanosleep") == 0 ) + proc_nanosleep = (FuncPtr_nanosleep)(s->n_value + slide); + else if ( strcmp(name, "_mach_port_allocate") == 0 ) + proc_mach_port_allocate = (FuncPtr_mach_port_allocate)(s->n_value + slide); + else if ( strcmp(name, "_mach_msg") == 0 ) + proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide); + else if ( strcmp(name, "_kill") == 0 ) + proc_kill = (FuncPtr_kill)(s->n_value + slide); + else if ( strcmp(name, "_getpid") == 0 ) + proc_getpid = (FuncPtr_getpid)(s->n_value + slide); + else if ( strcmp(name, "_OSAtomicCompareAndSwap32") == 0 ) + proc_OSAtomicCompareAndSwap32 = (FuncPtr_OSAtomicCompareAndSwap32)(s->n_value + slide); } } } +#endif -//void coresymbolication_unload_image(void*, const ImageLoader*); -void _Z28coresymbolication_load_imagePvPK11ImageLoadery(void* shm, void* image, uint64_t time) { - // look up function in host dyld just once - if ( sLoadPtr == NULL ) - findCSProcs(); - if ( sLoadPtr != NULL ) - (*sLoadPtr)(shm, image, time); -} +void xcoresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh) +{ + // if host dyld supports this notifier, call into host dyld + if ( gSyscallHelpers->version >= 4 ) + return gSyscallHelpers->coresymbolication_load_notifier(connection, timestamp, path, mh); +#if SUPPORT_HOST_10_10 + // otherwise use notifier code in dyld_sim + findHostFunctions(); + coresymbolication_load_notifier(connection, timestamp, path, mh); +#endif +} -//void coresymbolication_load_image(void**, const ImageLoader*, uint64_t); -void _Z30coresymbolication_unload_imagePvPK11ImageLoader(void* shm, void* image) { - // look up function in host dyld just once - if ( sUnloadPtr == NULL ) - findCSProcs(); - if ( sUnloadPtr != NULL ) - (*sUnloadPtr)(shm, image); +void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh) +{ + // if host dyld supports this notifier, call into host dyld + if ( gSyscallHelpers->version >= 4 ) + return gSyscallHelpers->coresymbolication_unload_notifier(connection, timestamp, path, mh); +#if SUPPORT_HOST_10_10 + // otherwise use notifier code in dyld_sim + findHostFunctions(); + coresymbolication_unload_notifier(connection, timestamp, path, mh); +#endif } diff --git a/src/libdyld_data_symbols.dirty b/src/libdyld_data_symbols.dirty new file mode 100644 index 0000000..85d1a92 --- /dev/null +++ b/src/libdyld_data_symbols.dirty @@ -0,0 +1,12 @@ +__ZL12sGlobalMutex +__ZZ26NSVersionOfLinkTimeLibraryE2mh +__ZZ33_dyld_register_func_for_add_imageE1p +__ZZ36_dyld_register_func_for_remove_imageE1p +__ZZ21_dyld_get_image_slideE1p +__ZZ6dlopenE1p +__ZZ40dyld_register_image_state_change_handlerE1p +__ZZ21_dyld_fast_stub_entryPvlE1p +__ZZ34dyld_image_path_containing_addressE1p +__ZZ20_NSGetExecutablePathE1p +_myDyldSection +_tlv_terminators_key diff --git a/src/stub_binding_helper.s b/src/stub_binding_helper.s index b00937e..de2d90b 100644 --- a/src/stub_binding_helper.s +++ b/src/stub_binding_helper.s @@ -180,9 +180,9 @@ _stub_binding_helper: #endif -#if __arm__ +#if __arm__ && !__ARM_ARCH_7K__ /* - * This is the interface for the stub_binding_helper for ARM: + * This is the interface for the old stub_binding_helper for ARM: * The caller has pushed the address of the a lazy pointer to be filled in with * the value for the defined symbol and pushed the address of the the mach * header this pointer comes from. diff --git a/src/threadLocalHelpers.s b/src/threadLocalHelpers.s index 625c320..683c5a8 100644 --- a/src/threadLocalHelpers.s +++ b/src/threadLocalHelpers.s @@ -280,24 +280,34 @@ LlazyAllocate: #endif -#if 0 #if __arm__ // returns address of TLV in r0, all other registers preserved .globl _tlv_get_addr .private_extern _tlv_get_addr _tlv_get_addr: - push {r1,r2,r3,r7,lr} - mov r7,r0 // save descriptor in r7 + push {r1,r2,r3,r7,lr} +#if __ARM_ARCH_7K__ + sub sp, sp, #12 // align stack to 16 bytes +#endif + mov r7, r0 // save descriptor in r7 ldr r0, [r7, #4] // get key from descriptor bl _pthread_getspecific // get thread value cmp r0, #0 bne L2 // if NULL, lazily allocate +#if __ARM_ARCH_7K__ + vpush {d0, d1, d2, d3, d4, d5, d6, d7} +#endif ldr r0, [r7, #4] // get key from descriptor bl _tlv_allocate_and_initialize_for_key +#if __ARM_ARCH_7K__ + vpop {d0, d1, d2, d3, d4, d5, d6, d7} +#endif L2: ldr r1, [r7, #8] // get offset from descriptor add r0, r1, r0 // add offset into allocation block - pop {r1,r2,r3,r7,pc} +#if __ARM_ARCH_7K__ + add sp, sp, #12 #endif + pop {r1,r2,r3,r7,pc} #endif .subsections_via_symbols diff --git a/src/threadLocalVariables.c b/src/threadLocalVariables.c index 01a73eb..e0e2884 100644 --- a/src/threadLocalVariables.c +++ b/src/threadLocalVariables.c @@ -78,7 +78,7 @@ typedef void (*TermFunc)(void*); -#if __has_feature(tls) || __arm64__ +#if __has_feature(tls) || __arm64__ || __arm__ typedef struct TLVHandler { struct TLVHandler *next; diff --git a/unit-tests/build-iPhoneOS-unit-tests b/unit-tests/build-iPhoneOS-unit-tests index 93cb583..f597f13 100755 --- a/unit-tests/build-iPhoneOS-unit-tests +++ b/unit-tests/build-iPhoneOS-unit-tests @@ -17,7 +17,7 @@ echo "#!/bin/sh" > /var/root/testing/run-all-tests chmod +x /var/root/testing/run-all-tests # do every combination of OS version and architectures -for os in "7.0" +for os in "8.0" do for arch in armv7 arm64 do diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 40cfde7..3d6535a 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -24,15 +24,18 @@ IOSROOT = ifeq "$(OS_NAME)" "iPhoneOS" #IOSROOT = $(shell xcodebuild -version -sdk iphoneos.internal Path) - IOSROOT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.0.Internal.sdk + IOSROOT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.Internal.sdk CC = $(shell xcrun -sdk iphoneos.internal -find cc) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) CXX = $(shell xcrun -sdk iphoneos.internal -find c++) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) LIPO = $(shell xcrun -sdk iphoneos.internal -find lipo) STRIP = $(shell xcrun -sdk iphoneos.internal -find strip) INSTALL_NAME_TOOL = $(shell xcrun -sdk iphoneos.internal -find install_name_tool) else - CC = $(shell xcrun -find cc) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) - CXX = $(shell xcrun -find c++) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) + ifeq "$(OSX_SDK_ROOT)" "" + OSX_SDK_ROOT = $(shell xcodebuild -version -sdk macosx.internal Path) + endif + CC = $(shell xcrun -find cc) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) -isysroot $(OSX_SDK_ROOT) + CXX = $(shell xcrun -find c++) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) -isysroot $(OSX_SDK_ROOT) LIPO = $(shell xcrun -find lipo) STRIP = $(shell xcrun -find strip) INSTALL_NAME_TOOL = $(shell xcrun -find install_name_tool) diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index d1df892..397178c 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -7,6 +7,8 @@ cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` CRSTATE=`defaults read com.apple.CrashReporter DialogType` defaults write com.apple.CrashReporter DialogType basic +SDK=`xcodebuild -version -sdk macosx.internal Path` + # run test targeting different OS versions for OSVERSION in 10.9 10.8 10.7 10.6 10.5 10.4 do @@ -17,7 +19,7 @@ do ../bin/make-recursive.pl clean > /dev/null # build default architecture - ../bin/make-recursive.pl ARCH="i386" OS_VERSION=$OSVERSION OS_NAME=MacOSX | ../bin/result-filter.pl + ../bin/make-recursive.pl ARCH="i386" OS_VERSION=$OSVERSION OS_NAME=MacOSX OSX_SDK_ROOT=${SDK} | ../bin/result-filter.pl # if 64-bit capable Intel, then also run all test cases for 64-bits if [ `sysctl -n hw.optional.x86_64` = "1" ] @@ -29,7 +31,7 @@ do ../bin/make-recursive.pl clean > /dev/null # build x86_64 architecture - ../bin/make-recursive.pl ARCH="x86_64" OS_VERSION=$OSVERSION OS_NAME=MacOSX | ../bin/result-filter.pl + ../bin/make-recursive.pl ARCH="x86_64" OS_VERSION=$OSVERSION OS_NAME=MacOSX OSX_SDK_ROOT=${SDK} | ../bin/result-filter.pl fi done diff --git a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c index a43882f..26a1e09 100644 --- a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c +++ b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c @@ -35,7 +35,6 @@ extern int foo(); int main(int argc, const char* argv[]) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED if ( argc > 2 ) { bool found = false; uint32_t count = _dyld_image_count(); @@ -57,7 +56,6 @@ int main(int argc, const char* argv[]) if ( actualResult != expectedResult ) FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); else -#endif PASS("DYLD_VERSIONED_FRAMEWORK_PATH-basic"); return EXIT_SUCCESS; diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c index 224d93e..a903b04 100644 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c @@ -34,14 +34,12 @@ extern int foo(); int main(int argc, const char* argv[]) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED int expectedResult = atoi(argv[1]); int actualResult = foo(); //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); if ( actualResult != expectedResult ) FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); else -#endif PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); return EXIT_SUCCESS; diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c index 224d93e..a903b04 100644 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c @@ -34,14 +34,12 @@ extern int foo(); int main(int argc, const char* argv[]) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED int expectedResult = atoi(argv[1]); int actualResult = foo(); //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); if ( actualResult != expectedResult ) FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); else -#endif PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); return EXIT_SUCCESS; diff --git a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c index 224d93e..a903b04 100644 --- a/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c +++ b/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c @@ -34,14 +34,12 @@ extern int foo(); int main(int argc, const char* argv[]) { -#if __MAC_OS_X_VERSION_MIN_REQUIRED int expectedResult = atoi(argv[1]); int actualResult = foo(); //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); if ( actualResult != expectedResult ) FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); else -#endif PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); return EXIT_SUCCESS; diff --git a/unit-tests/test-cases/image_header_containing_address/Makefile b/unit-tests/test-cases/image_header_containing_address/Makefile new file mode 100644 index 0000000..a9c79c1 --- /dev/null +++ b/unit-tests/test-cases/image_header_containing_address/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/image_header_containing_address/foo.c b/unit-tests/test-cases/image_header_containing_address/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/unit-tests/test-cases/image_header_containing_address/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/unit-tests/test-cases/image_header_containing_address/main.c b/unit-tests/test-cases/image_header_containing_address/main.c new file mode 100644 index 0000000..56312cd --- /dev/null +++ b/unit-tests/test-cases/image_header_containing_address/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void foo(); + + +// checks dladdr() and dyld_image_header_containing_address() return same image +static void verify(void* addr) +{ + Dl_info info; + if ( dladdr(addr, &info) == 0 ) { + FAIL("dladdr(%p, xx) failed", addr); + exit(0); + } + const struct mach_header* mh = dyld_image_header_containing_address(addr); + if ( mh != info.dli_fbase ) { + FAIL("dladdr's dli_fbase != dyld_image_header_containing_address()"); + exit(0); + } +} + + +int main() +{ + verify(&main); + verify(&foo); + + int x; + if ( dyld_image_path_containing_address(&x) != NULL ) { + FAIL("dyld_image_header_containing_address() of stack variable not NULL"); + exit(0); + } + + PASS("dyld_image_header_containing_address"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/interpose-not-inserted/Makefile b/unit-tests/test-cases/interpose-not-inserted/Makefile new file mode 100644 index 0000000..cccf28c --- /dev/null +++ b/unit-tests/test-cases/interpose-not-inserted/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libmystrdup.dylib + +main : main.c wrap.c mystrdup.c + ${CC} ${CCFLAGS} -dynamiclib mystrdup.c -o libmystrdup.dylib + ${CC} ${CCFLAGS} -dynamiclib wrap.c -o libwrap.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main libwrap.dylib libmystrdup.dylib main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main libmystrdup.dylib libwrap.dylib + diff --git a/unit-tests/test-cases/interpose-not-inserted/main.c b/unit-tests/test-cases/interpose-not-inserted/main.c new file mode 100644 index 0000000..c71ffb6 --- /dev/null +++ b/unit-tests/test-cases/interpose-not-inserted/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern char* wrap_strdup(const char*); + +int main() +{ + const char* x = strdup("123"); + const char* y = wrap_strdup("456"); + + if ( (strcmp(x, "hello") == 0) && (strcmp(y, "hello") == 0) ) + PASS("interpose-not-inserted"); + else + FAIL("interpose-not-inserted"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/interpose-not-inserted/mystrdup.c b/unit-tests/test-cases/interpose-not-inserted/mystrdup.c new file mode 100644 index 0000000..18041ac --- /dev/null +++ b/unit-tests/test-cases/interpose-not-inserted/mystrdup.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + +char* mystrdup(const char* in) +{ + return strdup("hello"); +} + +DYLD_INTERPOSE(mystrdup, strdup) diff --git a/unit-tests/test-cases/interpose-not-inserted/wrap.c b/unit-tests/test-cases/interpose-not-inserted/wrap.c new file mode 100644 index 0000000..f634ab2 --- /dev/null +++ b/unit-tests/test-cases/interpose-not-inserted/wrap.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +// 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); +} + -- 2.45.2